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.
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: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
Publicar un comentario