MidJourney prompt: isometric magical spellbook::0.5 old tome::1 book with a picture of a crab on the cover laying on a table next to open fire
Officially, the way to started with Rust is to read The Book.
Normally I skip over documentation and get right down to experimenting with the language: Trying it out with small projects, tinkering and reading blog posts. This path usually leads me back to the documentation. Once I’ve tried, and failed, and tried again I’ll come back to actually figure out how to achieve a goal. Diving in isn’t wasted effort but likely we could get there faster by listening first. It was no different for me with Rust.
Except perhaps that I’ve been more impressed with the quality of the official documentation. It’s a really great starting place.
Let me say that again:
The official Rust book is really well written and approachable and is the best place to start learning the language.
Yes, you should read it.
Here I’ll talk through what I found immediately valuable, the projects.
Building a guessing game
This one-pager introduces us to a bunch of fundamental concepts and gets our fingers moving.
We start our project like all the others:
cargo new guessing_game
We add the rand package:
cargo add rand
Then we write the code to loop through input to see if the user’s number matches our randomly generated one. We learn how to handle invalid input and refactor a bit of the mess. Neat little program and great place to start.
Again, let’s summarize what’s happening. The code above commands the user to “Guess the number!”; then generates a `secret_number` using the rand crate we installed. The integer here is between one and one hundred and is generated off of the `thread_rng`. I was curious about that last method and what it’s doing, here’s what the docs have to say:
Function rand::thread_rng
Retrieve the lazily-initialized thread-local random number generator, seeded by the system. Intended to be used in method chaining style, e.g.
thread_rng().gen::<i32>()
, or cached locally, e.g.let mut rng = thread_rng();
. Invoked by theDefault
trait, makingThreadRng::default()
equivalent.
So useful for retrieving iterations of random numbers, should we need them. We only need one at a time here.
Then, while in a loop we retrieve the user’s guess—as a string. We trim the string and parse it, in case the user has given us some spaces in their entry. We mutate that guess into an unsigned integer thirty-two. If we couldn’t parse or trim this string or there was some kind of error we just continue and loop around again.
The loop continues, guiding the guesser if their entry was too big or small.
Eventually “You win!” is printed when the user finally lands on the integer.
Building a command line program (grep)
Now let’s scale up in complexity and introduce how we might refactor to isolate concerns and test our code. We do this by building `grep` (globally search a regular expression and print) which searches a given file for a string. It then prints the lines it finds.
The `std::env::args` function from the standard library gives us what we need to grab provided arguments—which we collect and store in a vector of strings:
let args: Vec<String> = env::args().collect();
In the course of building out the program we introduce a Config struct and since we know how the inputs relate to the string and file we’d like to check, we can create this data structure.
Unlike with tuples, in a struct you’ll name each piece of data so it’s clear what the values mean. Adding these names means that structs are more flexible than tuples: you don’t have to rely on the order of the data to specify or access the values of an instance. Source.
Small interlude here to cite some feedback Rust is giving us in the terminal. It’s just a joy to see this kind of feedback. Here we stipulated returning a tuple of strings but in actuality we’re returning a Config struct.
The feedback Rust gives you is truly a joy.
It doesn’t take a lot of code from there to read the contents of a file, search it for the desired text and print everything to standard out. At this point most tutorials would declare victory, perhaps mention followups that could be implemented and then go to lunch. Not so for the Rust book. This is the time where Rust dives into refactoring for modularity and error handling.
Herein proceeds a journey to refactor the code we threw together and extract logic into a library. The iterative refactoring done on the code is sometimes a slog, but the feedback from Rust itself (and literally having the Book open) will help you through it. Also some Github users have explicitly documented the process in repos like this one here.
A bit cryptic but rationale. If you don’t go back and state these functions and structs are public then then are private.
Testing the thing
At this point we’re ready for some test driven development (TDD). In this process we’ll write a test and see it failing for expected reasons, then get it passing while refactoring.
The approach to unit testing here is different than what you typically see in say, a python codebase, the unit test goes right at the end of the file (is this the Rust way to do it? I don’t know) and is separated from the main code by a #[cfg(test)]
attribute. The test itself is marked with a #[test] attribute. Here’s what the final test looks like:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_result() {
let query = "duct";
let contents = "\Rust:safe, fast, productive. Pick three.";
assert_eq!(vec!["safe, fast, productive."],
search(query, contents));
}
}
So the infrastructure around the test is something to get acquainted with but even with some light coding history you will recognize that the business piece of the test occurs in the `assert_eq` block.
The Book leads you through creating the test and going from:
Red light
To:
Green light
At this point there are some nice-to-haves that we can clean up but I’m just playing around with this neat program Rust helped me build.
Initial thoughts on The Book
There’s plenty more to the book than these two projects and again I’ll encourage you to read it. Hopefully this post has shown diving in isn’t so daunting. As mentioned, practicing first with a project or two could help provide context with you’re learning. I know I need my fingers to do the learning.
And these projects are good first projects to start with—being a mixture of scaling difficulties, variety of application, and covering major concepts you’re likely to see in the course of your work with Rust.
I’ll be back to read the other chapters of the Book covering types, structs, stack vs heap memory and the final project of the book which covers some web work.
Let me know
So let me know what you think. Should we do more projects or switch tacts and dive into the the fundamentals of the language. Maybe more of everything?
Thanks for reading.