A.1Useful crates and tools
Rust's standard library is deliberately small. It gives you the language's core types, collections, threads, file and network IO, and not much beyond that. Almost everything else, JSON, HTTP, command-line parsing, dates, random numbers, fast parallel loops, lives in crates: external libraries you pull in with cargo add (lesson 7.9) from crates.io, the package registry. That smallness is a feature. The standard library has to keep working forever, so it moves slowly and stays conservative; the ecosystem can experiment, iterate, and settle on the best design.
This appendix is a map of the territory, not a tutorial. It points you at the crates and tools you'll meet first and most often, with a sentence or two on what each is for and when to reach for it. None of it is required reading, and you've already used a few of these in the course. Think of it as the page to come back to when you start a real project and wonder "surely someone has already solved this."
A living document
The ecosystem moves, and so does this page. Crate versions and even which crate is the popular choice for a job change over time. Treat the names here as starting points, not gospel, and check crates.io and lib.rs for what's current and well-maintained before you commit to a dependency.
How to size up a crate
Before the specifics, the skill that outlasts any individual recommendation: how to tell a good crate from a risky one. When you find a candidate on crates.io, look at four things. Recent downloads tell you how many other people are relying on it (a crate with millions of downloads has had its bugs found). The last release date tells you whether it's still maintained, or abandoned three years ago. The documentation on docs.rs tells you whether the authors cared enough to explain it; every crate gets its docs built and hosted there automatically. And the version number tells you about stability: a 1.0 or higher crate promises not to break your code on minor updates (lesson 13.6 covered semantic versioning), while a 0.x crate is still allowed to change its API between releases.
One more habit worth forming: prefer fewer, well-chosen dependencies over many casual ones. Every crate you add is code you're trusting and a thing that can break, so it's worth a moment's thought rather than a reflex.
Serialization: serde
If you need to read or write JSON, or any structured data format, you want serde (it's a contraction of "serialize/deserialize"). It's one of the most-used crates in the entire ecosystem. You derive two traits on your own structs and serde handles converting them to and from a data format for you:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Config {
name: String,
retries: u32,
}
With those derives in place, a companion crate like serde_json turns a Config into a JSON string and back with one function call each. The same Config can be read from TOML, YAML, or a dozen other formats just by swapping the companion crate; serde itself is format-agnostic. Pull it in with cargo add serde --features derive alongside cargo add serde_json. This is the crate that makes the derive macros from chapter 16 pay off in real work.
Command-line parsing: clap
You parsed command-line arguments by hand in chapter 20 (lesson 20.1), reading std::env::args and matching on the strings yourself. That's the right way to learn what's happening, but for a real tool you'll want clap (the major version to use is 4). You describe your arguments as a struct with attributes, and clap generates the parser, the --help output, error messages for bad input, and even shell completions:
use clap::Parser;
#[derive(Parser)]
struct Args {
/// The pattern to search for
pattern: String,
/// The file to search in
path: String,
}
fn main() {
let args = Args::parse();
// args.pattern and args.path are filled in for you
}
That doc comment above each field becomes the help text automatically. If you build the minigrep tool from chapter 20 into something you'd actually use, clap is the upgrade.
Error handling: anyhow and thiserror
Chapter 12 built error handling from Result and your own error enums up. In practice two crates by the same author smooth over the boilerplate, and which you pick depends on what you're building. anyhow is for applications: when you just want errors to propagate with good context and don't care about their exact types, anyhow::Result<T> lets any error flow up through ? and lets you attach .context("while loading config") notes that turn into readable reports. thiserror is for libraries: when callers need to match on specific error variants, it derives the Error trait and Display impls for your custom error enum so you don't write them by hand. The rule of thumb: binaries reach for anyhow, libraries reach for thiserror, and large projects often use both.
Parallelism and async: rayon and tokio
You met both of these in the concurrency chapters. rayon (lesson 22.5) turns a sequential iterator into a parallel one by changing .iter() to .par_iter(), spreading the work across all your CPU cores with no manual thread management. Reach for it whenever you have a big computation over a collection. tokio (lesson 23.3) is the de facto async runtime, the engine that actually drives the futures from chapter 23. Anything doing a lot of network IO, a web server, an API client, a chat backend, is almost certainly built on tokio.
HTTP and the web
To make HTTP requests (talk to an API, download a file), reqwest is the standard client. Note that it's still pre-1.0 (the current series is 0.13), so it may make breaking changes between releases. To serve HTTP, you built a server from raw sockets in chapter 26 to see how it works; for real services people use a framework like axum (which is built on tokio by the same team) or actix-web. Both handle routing, request parsing, and the concurrency for you.
A few more worth knowing
These come up constantly across all kinds of projects: regex for regular expressions; chrono or the newer time crate for dates and timestamps (the standard library only does raw durations); rand for random numbers, which you already used in chapter 7; itertools for iterator adapters the standard library doesn't include; and tracing for structured logging and diagnostics in larger applications.
Command-line tools
Not everything ships as a crate. Some of the most useful pieces of the ecosystem are tools you install once and run from the terminal. You've already met Clippy (lesson 0.11), the linter that catches mistakes and suggests more idiomatic code, and rustfmt (cargo fmt), which formats your code to the community standard so you never argue about style. Beyond those, rust-analyzer is the language server that powers autocomplete, inline errors, and go-to-definition in your editor (lesson 0.7); install it and your editor becomes dramatically more helpful. cargo-watch re-runs a command every time you save a file, so cargo watch -x test gives you a test suite that runs itself as you type. And cargo-edit adds handy subcommands for managing dependencies, though cargo add is now built in.
Cross-compilation
One of Rust's quiet strengths is producing a binary for a platform you're not on, building a Linux executable from your Mac, say, or targeting a Raspberry Pi from a desktop. Rust calls each platform a target, and you add one with rustup:
$ rustup target add aarch64-unknown-linux-gnu
$ cargo build --target aarch64-unknown-linux-gnu
That's enough for pure-Rust code. The wrinkle is that targeting a different operating system usually also needs that system's linker and C libraries, which rustup doesn't provide. The community tool cross solves this by doing the build inside a preconfigured container, so cross build --target ... often just works where plain cargo would stop at a linker error. If you need to ship binaries for platforms other than your own, start there.
Where this leaves you
You now know enough Rust to read any of these crates' documentation and use them well, because under the hood they're all built from the same pieces this course covered: traits, generics, ownership, error types, iterators, closures. A crate is just someone else's code following the same rules as yours. The next appendix steps back to the language itself and explains editions, the mechanism that lets Rust evolve without breaking the code you've already written.