Capstone: Building a CLI Tool
One small, real command-line application, combining ownership, error handling, traits, and crates from across all three Rust courses.
The Project: A Task Tracker CLI
Add, list, complete, and remove tasks, persisted to a JSON file between runs, with subcommands parsed by the clap crate.
Project Setup (Cargo.toml)
The Course 2 Chapter 6 payoff — real, external crates pulled in from crates.io, exactly as covered there.
The Task Struct & Custom Error Type
Course 1 Chapter 7's error handling and Course 3 Chapter 1's trait implementation, combined directly: a real error type implementing Display and std::error::Error.
A Storage Trait for Persistence
Course 2 Chapter 2's trait basics chapter, now used for a genuine abstraction — a storage backend that could, in principle, be swapped for anything else implementing Storage.
CLI Parsing with clap
#[derive(clap::Parser)] and #[derive(clap::Subcommand)] are exactly the derive procedural macros Course 3 Chapter 4 explained — seen here in genuine, practical use, generating a complete argument parser from a plain struct/enum definition.
Tying It Together: main()
? propagates errors all the way up to main's own Result return type — Course 1 Chapter 7's error-handling philosophy, in its natural home: a real CLI tool that fails with a clean message instead of a panic when something goes wrong.
What This Capstone Demonstrates
| Feature | Where It Appears |
|---|---|
| Ownership (Course 1) | Task struct, Vec<Task> passed and mutated throughout main() |
| Error Handling (Course 1, Ch.7) | TaskError, Result<T, TaskError>, ? propagation to main |
| Traits (Course 2 Ch.2, Course 3 Ch.1) | The Storage trait and its JsonFileStorage implementation |
| Crates (Course 2, Ch.6) | clap and serde as real Cargo.toml dependencies |
| Procedural Macros (Course 3, Ch.4) | #[derive(Parser)], #[derive(Subcommand)], #[derive(Serialize)] |
The Bigger Picture
This closes the loop back to Course 1 Chapter 1's founding question: how does Rust deliver memory safety without a garbage collector? This small tool, working end to end, demonstrates the full arc — compile-time-verified ownership and borrowing, zero-cost abstractions (generics, iterators), and a real, practical ecosystem (crates.io, clap, serde) — Rust's whole pitch, delivered in working code rather than left as an abstract claim.
dyn Storage, Chapter 1) for supporting multiple storage backends interchangeably at runtime; async I/O (Chapter 3) if storage became a network service instead of a local file; unsafe (Chapter 2) only if genuine FFI were ever needed. This capstone is a real, usable starting point, not a toy that stops mattering once the course ends.
? and let main's own Result return type produce a clean failure message — not unwrap() its way through file I/O or parsing and crash with a raw panic on the first bad input. Course 1 Chapter 7's guidance, now shown in the exact context it was always meant for.
Coding Challenges
Add a Commands::Clear variant that removes all tasks, and wire it into main()'s match block, following the existing variants as a template.
📄 View solutionWrite a #[cfg(test)] mod tests block (Course 2, Chapter 7) with a unit test verifying that Commands::Complete correctly sets a task's completed field to true when given a valid id, and that an invalid id produces a TaskError::NotFound via #[should_panic] or explicit Result matching.
📄 View solutionWrite a short reflection: pick one feature from EACH of the three Rust courses (Fundamentals, Intermediate, Advanced) that you found most valuable, and explain in a sentence each why it changed how you'd approach writing code, in Rust or otherwise.
📄 View solutionChapter 6 Quick Reference
- A real CLI tool combines this track's material naturally: ownership, Result-based error handling, traits for abstraction, and real crates.io dependencies
- clap (with the derive feature) — declarative CLI parsing via #[derive(Parser)]/#[derive(Subcommand)]
- serde/serde_json — struct-to-JSON serialization via #[derive(Serialize, Deserialize)]
- A custom error enum implementing Display + std::error::Error is the idiomatic foundation for a real Result-based API
- The Storage trait demonstrates trait-based abstraction with a genuine, practical payoff — not just a teaching example
- fn main() -> Result<(), E> lets ? propagate all the way to the program's own exit, replacing panics with clean error output
🎉 Rust Advanced Complete — 6 / 6 chapters — Full Rust Track Complete
From cargo and ownership through borrowing, structs, enums, error handling, and collections (Fundamentals) — through lifetimes, traits, generics, smart pointers, concurrency, modules, and testing (Intermediate) — to advanced traits, unsafe code, async, macros, performance, and this capstone (Advanced): 21 chapters across three courses, one coherent design philosophy from start to finish. This completes the entire Rust track on the site.