Ir al contenido principal

I forced myself to read Rust code

 Hello peers in wisdom,

One month ago I found myself reading The passionate programmer by Chad Flower, here the author gives valuable advises for building a strong career as a software engineer (is not easy by the way).One of his insights inspired me to write this brief blog to share with whoever read this. The more or less I learn this week. 

As the title suggests, the advice is READ CODE. What kind of code? The good kind.

So... after dedicating around 100 hours to read the Rust book and resolve some basic exercises  I felt prepared enough to tackle some real code. I decided to search in the popular section of crates.io, and I found serde, a rust library for data serialization/deserialization. After 5 minutes of cloning the project and reading I was completely lost.

Are people who wrote this geniuses or did I just lose 100 hours of my life?

Fortunately (or not) it was a mix of both. Anyway, the next day I am back at crate.io and this time I chose something less popular.

Colors

Color is a library developed by Linebender, it helps to make operations with the different types or "color spaces" following the CSS color standard.  Lucky me. This time I was able to understand the code, in a basic level since I am not an color expert, and as Chad recommends I make notes, I outline the good and the bad; and adopted some good tricks from it.

The good

1. Execute code at compile time

This technique is great to optimize execution in run rime. It consist of using the reserved word const in the methods of a impl in this case. In this fragment of code its used in new and with_alpha methods but not in convert, why is that?

impl<CS: ColorSpace> OpaqueColor<CS> {

    pub const BLACK: Self = Self::new([0., 0., 0.]);
    pub const WHITE: Self = Self::new(CS::WHITE_COMPONENTS);

    /// Create a new color from the given components.
    pub const fn new(components: [f32; 3]) -> Self {
        let cs = PhantomData;
        Self { components, cs }
    }

    /// Convert a color into a different color space.
    #[must_use]
    pub fn convert<TargetCS: ColorSpace>(self) -> OpaqueColor<TargetCS> {
        OpaqueColor::new(CS::convert::<TargetCS>(self.components))
    }

    /// Add an alpha channel.
    ///
    /// This function is the inverse of [`AlphaColor::split`].
    #[must_use]
    pub const fn with_alpha(self, alpha: f32) -> AlphaColor<CS> {
        AlphaColor::new(add_alpha(self.components, alpha))
    }
...

The answer is that the functions that are called inside them must be also declared as const. In convert function the CS::convert function is not const, therefore does not allow to the method convert be const, consequently it can not be run at compile time.

This idea of creating data at compile time and just accessing to it in run time is also used for generating a minimal perfect hash lookup table for the x11 palette colors. In this lib is used a python file to create the hash table, probably because is more easy to write python or may be for optimization reasons... ?

Then the code and data produced by the python program is pasted into a rust fille inside the project.

This way is not necessary to generate the data for hashing at run time. Better performance!


The bad (and not so)

This is a hardcode function to do the multiplication a 3x3 matriz a 3 vector.

/// Multiplication `m * x` of a 3x3-matrix `m` and a 3-vector `x`.
const fn matvecmul(m: &[[f32; 3]; 3], x: [f32; 3]) -> [f32; 3] {
    [
        m[0][0] * x[0] + m[0][1] * x[1] + m[0][2] * x[2],
        m[1][0] * x[0] + m[1][1] * x[1] + m[1][2] * x[2],
        m[2][0] * x[0] + m[2][1] * x[1] + m[2][2] * x[2],
    ]
}

Pros:

  • Easy to write 
  • Easy to read

Cons:

  • Not scalable (it probably does not need to be, since color composition is just R - G - B) 
  • It could be written with a loop

In my opinion it is fine and I would not start a PR. I just get distracted by an easy-hardcode solution.

Tricks I learned

For parsing integers to float-types in rust, there is a pretty easy way:

    const SRGB_TO_BRADFORD: [[f32; 3]; 3] = [
        [
            1_298_421_353. / 3_072_037_500.,
            172_510_403. / 351_090_000.,
            32_024_671. / 1_170_300_000.,
        ],
        [
            85_542_113. / 1_536_018_750.,
            7_089_448_151. / 7_372_890_000.,
            244_246_729. / 10_532_700_000.,
        ],
        [
            131_355_661. / 6_144_075_000.,
            71_798_777. / 819_210_000.,
            3_443_292_119. / 3_510_900_000.,
        ],
    ];

Just add a period at the end of the number. In this function since the return type is an vector of f32, the numbers will be parsed to f32.


Conclusion

It is satisfactory to understand how the tools we use work, and even more to learn code tricks from them!
I will be writing these blogs in a way of record my baby steps in rust environment and hoping it can be useful to someone.

-Alan


Comentarios

Entradas populares de este blog

Applying DDD on pricing goods at supermarket

Hello peers in wisdom, I hope you had a wonderful week. Todays topic consist on exercising a non technical part of software. Domain-Driven Design DDD is a set of tools that makes easier to understand business experts and then translate their knowledge domain to our domain (software). I encourage you to learn more by reading and watching the resources I found useful, they are at the bottom of the post. Pricing goods The definition of the problem is on this  kata . The goal, basically, is think about functionalities, edge cases and implementation for pricing goods at a supermarket. Steps I followed     Event Storming . This method helped me to start defining what where the things I expected to happen in this system. In the image you can identify them by the salmon color. Its important to note that at the bottom right is defined the meaning of each box color.           Commands and actors . The next step was to ask who makes what . In this case ...

Navigating AI generated code

Hello peers in wisdom, Last week I was experimenting for the first time with AI tools to generate code, specifically with the code editor, Antigravity. I'll describe my configurations, experiences, expectations and results.  What was my setup?  Anti gravity out of the box and I used Gemini 3 Flash. I give it a detailed requirement document for a automated quoting system. What were my expectations? Semi functional project Clean hexagonal architecture Use of libraries I indicated it What was the result? It was not so good not so bad.      1. The project is semi functional as I expected, some of the most interrelated business rules were not exactly implemented as it was expected. CRUD and authentication systems were fully functional.     2. The code architecture was hexagonal as I asked for, but there were bad practices implemented such as anemic entities, use of 'any' instead of mapping with a DTO.  Also there was a lack of customs errors and data v...