Thirty-seven lessons, starting from fn main() { println!("Hello, World!"); } and ending with the macro that makes it work. This last one teaches no new syntax. First, a look back - then two small tools that belong in every Rust project from here on, one thing this course deliberately never showed you, and a map of where it leads next.
A shape, in hindsight
A few threads ran the entire length of this course, each picked up exactly when it mattered. Custom-error-types wrapped errors in Box<dyn Error> and asked you to trust it; ten lessons later, smart-pointers named both halves - and previewed Arc<Mutex<T>>, which became shared-state's real code two lessons later. Closures' move and lifetimes' 'static became fearless-concurrency's real E0373 error. And lesson 1's very first ! waited 35 lessons for last lesson.
None of it was filler. Ownership underwrites every borrow-checker error since, including the Send/Sync errors two lessons ago. Option, Result, and ? underwrite every fs::read_to_string and serde_json::from_str in the practical-Rust section. Iterators are the .map/.filter/.enumerate in building-a-cli's list command, and move closures are what gets handed to every thread::spawn and tokio::spawn since. If generics, traits, and lifetimes felt like the hardest stretch the first time and easier the fifth time they came back - that was the plan, not a coincidence.
Two commands for every project
One tool has been here since lesson 3, quietly: rustup installed more than the compiler. Two of its companions never came up:
This compiles cleanly - cargo build has nothing to say about it. cargo clippy does:
Two warnings, neither one an error - the compiler was satisfied; clippy has opinions beyond "does this compile." v.len() == 0 works fine, but v.is_empty() says the same thing more directly - clippy knows the idiom. The second one is more pointed: vec![1, 2, 3], the exact macro last lesson spent an entire lesson explaining, isn't even the best tool here - this particular list never grows, so a plain array would do. Clippy knows the idiom and the exception.
cargo fmt is the other half: it reformats every file in a project to one standard style - the same style every code block in this course has used - so two Rust developers' code looks the same on sight, and nobody argues about brace placement.
The third one, rustup doc, opens a local copy of the Rust Book and the full standard library documentation in your browser - no internet required. It's been sitting on disk since lesson 3.
One thing this course never showed you
The word unsafe came up twice, in passing. Understanding-errors called it "rare in everyday application code, far more common in the systems-level Rust this language was originally built for." Shared-state, two lessons ago, ran ten threads against one counter and got Result: 10 every time, "and not one line of unsafe." Neither lesson said what it actually looks like. This is what it actually looks like:
Run it: 6. *mut i32 is a raw pointer - unlike every reference this course has used, it has no borrow-checker rules attached: no guarantee it points anywhere valid, no tracking of how long it lasts, no exclusivity check. Creating one is safe; using it - dereferencing it with *r - requires an unsafe block, Rust's way of saying "the compiler can't verify this is sound; you're asserting that it is." That's the entire feature. There's no special syntax inside the block beyond this - just a handful of operations (raw pointer access, calling other unsafe functions, a couple more) that the compiler will only allow inside one.
Reach for it almost never in application code - but it's not exotic. It's how Vec, String, Mutex, and Rc are built underneath: a small unsafe core, wrapped in a safe API, so that everything you've written could be 100% safe Rust on top of it. Now you know the word, the syntax, and roughly where it hides.
Where this course fits
rand - lesson 4's very first dependency - serde, and tokio were real crates from the wider Rust ecosystem, not built for this course. A few more, mapped to what you already know:
None of these need to be memorized - docs.rs hosts documentation for every crate on crates.io, generated by the same cargo doc from the comments lesson. When a project needs something, search there first; chances are good it already exists; reading its types and traits will look exactly like reading the standard library, because it's the same language. Beyond that: users.rust-lang.org for questions, and This Week in Rust for a weekly pulse on everything moving in the language and ecosystem.
Quick exercise
Pick one, and extend task_cli with it - you now have everything each one needs:
- Add
clapwith thederivefeature, and replace theenv::argsmatch with a derivedstruct Cli. Same three commands, declared instead of parsed by hand. - Add a
search <term>command: filtertaskswith.iter().filter(...)on whether the description contains the term, then reuse thelistcommand's.enumerate()printing. - Wrap the in-memory
Vec<Task>inArc<Mutex<Vec<Task>>>and save to disk on a background thread viathread::spawn, soaddanddonereturn immediately. - Add
axumandtokio, and serveGET /tasksas JSON straight fromload_tasks- the sameTaskstruct,#[derive(Serialize)]already done, now answering over HTTP instead of the terminal.
Lesson 9 said a borrow-checker error "doesn't just say 'you messed up' - it tells you exactly where, why, and which line is the problem," and that this is what people mean by the compiler teaching you. Every error since - through threads, futures, and macros - was that same compiler, same habit. That doesn't stop here.
Back to the course overview - or pick whichever lesson still feels fuzzy and read the compiler's explanation again. It hasn't gotten any less honest, and by now, neither have you.