Book review: The Rust Programming Language
I mentioned last month that I am learning Rust. After completing the Rustlings I started a small personal project to get some practice. I spent about 12 hours on it, but I progressed slowly. I constantly bumped into minor difficulties that slowed down my progress; it became clear that my understanding of Rust was lacking. I needed to learn more. I picked up the book Rust Programming Language (official site), it has an excellent reputation online and seems to be the de facto Rust reference for beginners. I wanted to solidify my theoretical understanding of the language.
While the Rust book is intended to be read by a wide audience, including relative neophytes, it goes deeply into some of the Rust concepts I wanted to understand better.
The first important thing I learned was the ownership model. In Rust every bit of data has a single owner and ownership gets transferred over when one doesn’t use a reference. I hadn’t grasped ownership and moving values before reading the book.
Then the borrow checker and its rules was another thing I got to understand better reading the book. It’s fine to have multiple immutable references, but only a single mutable reference is allowed. In my first Rust project I cloned most of the data because the ownership and borrowing rules were unclear. I realized that I didn’t need all those clone calls and optimized my project’s memory use.
Building on these rules, I also learned that shadowing is not only fine but encouraged in Rust, unlike in languages like Go, C, or Python where it’s often avoided. Rust ownership and borrowing rules make shadowing more ergonomic. For instance, here’s how shadowing can simplify handling an Option<String>
without needing multiple variable names:
fn process_input(input: Option<String>) -> String {
// Shadow to provide default
let input = input.unwrap_or_else(|| "default".to_string());
// Shadow to transform (immutable borrow)
let input = input.trim();
input.to_uppercase()
}
Here shadowing makes sense since mutability in Rust is restricted and having to come up with new names for every value extracted from Option
or Result
can make the program less readable.
Finally I learned that Rust’s pattern matching with match
is more powerful than I anticipated. match
can destructure structs, enums, and nested types; it has a match guard feature giving it extra expressivity, and it can bind values nested in complex types:
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Found an id in range: {id_variable}"),
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range")
}
Message::Hello { id } => println!("Found some other id: {id}"),
}
Reading the book took me about ten days, and I think it was well worth it. I understand Rust’s constraints better and discovered it was more expressive that I thought. I am more confident reading and writing Rust code, my project is moving forward at a better pace and I am having a ton of fun with it.
The Rust Programming Language is a classic for excellent reasons. If you’re diving into Rust, it’s an essential read that will boost your understanding and enjoyment, just like it did for me.