Skip to content
AyoKoding

Overview

What This Tutorial Teaches

This tutorial teaches Rust CLI development through 80 heavily annotated, self-contained code examples. The target audience is engineers who already know Java, Python, Go, or JavaScript/TypeScript well and want to read and write real Rust CLI codebases without getting stuck on Rust's unique concepts.

After completing all 80 examples, you can read any production Rust CLI tool—including real projects like ripgrep, fd, cargo, and bat—and understand every line. You can write your own CLIs that handle files, directories, serialization, error propagation, and testable output.

This tutorial focuses exclusively on synchronous Rust. Async/await is a separate, steep learning curve that belongs in a dedicated tutorial. All 80 examples compile and run with stable Rust.

Why This Tutorial Exists

The three biggest struggles for GC-language engineers learning Rust are:

  1. Ownership and borrowing — 8 full examples use CLI-domain scenarios (processing command names, reading config paths, accumulating validation results) rather than toy memory examples that feel disconnected from real work.

  2. Lifetimes — This tutorial deliberately avoids teaching lifetime annotations. Every example uses owned types (String, Vec<T>, HashMap) so the borrow checker never demands annotations. You build intuition for ownership without the additional complexity of annotation syntax.

  3. Type system and trait thinking — Each trait example explicitly contrasts Rust traits with Java interfaces (nominal subtyping) and Go interfaces (structural duck-typing). Rust uses structural dispatch like Go but with explicit impl declarations like Java.

Edition and Toolchain

All examples target Rust 2024 edition (stable since Rust 1.85). Important Edition 2024 changes reflected throughout:

  • gen is a reserved keyword — no example uses it as an identifier
  • static mut requires unsafe — examples use LazyLock and OnceLock instead
  • std::env::set_var is unsafe — examples avoid it or wrap properly
  • Match ergonomics improved — examples follow current idioms

Key Crates

Every crate used in this tutorial is stable, widely adopted, and production-standard:

CrateVersionPurpose
clap4.6.xCLI argument parsing with derive API
anyhow1.xError handling for CLI applications
serde1.xSerialization framework
serde_json1.xJSON serialization
serde_yml0.0.xYAML serialization
walkdir2.xSimple directory traversal
ignore0.4.xGitignore-aware directory walking
regex1.xRegular expressions
assert_cmd2.xCLI integration testing
tempfile3.xTemporary files in tests

Learning Path

%% Color Palette: Blue #0173B2, Orange #DE8F05, Teal #029E73, Purple #CC78BC, Brown #CA9161
graph TD
    A["Beginner<br/>Examples 1-28<br/>Rust Fundamentals via CLI Lens"] --> B["Intermediate<br/>Examples 29-57<br/>Real CLI Patterns"]
    B --> C["Advanced<br/>Examples 58-80<br/>Production CLI"]
 
    style A fill:#0173B2,color:#fff
    style B fill:#DE8F05,color:#fff
    style C fill:#029E73,color:#fff

Beginner (Examples 1-28): Rust Fundamentals via CLI Lens

Focus: Learn Rust fundamentals using CLI-domain examples throughout.

Every ownership example involves a command name, config path, or results accumulator—not abstract toy data. By example 28 you understand ownership, borrowing, traits, enums with data, pattern matching, Option<T>, Result<T, E>, the ? operator, Vec<T>, HashMap<K, V>, iterators, closures, and a first working CLI with clap.

Key topics: fn main(), variables, shadowing, types, String vs &str, functions, ownership, borrowing, mutable borrowing, slices, structs, impl blocks, #[derive], enums, enums with data, pattern matching, if let, Option, Result, ?, Vec, HashMap, iterators, closures, traits, first clap CLI, module system, unit tests.

Intermediate (Examples 29-57): Real CLI Patterns

Focus: The patterns that appear in every production Rust CLI.

Subcommands, global flags, file I/O, directory walking, regex, lazy globals, anyhow error handling, serde serialization, BTreeMap for deterministic output, testable output via dyn Write, environment variables, exit codes, output format enums, advanced iterators, and integration testing with assert_cmd and tempfile.

Key topics: Clap subcommands, nested subcommands, global flags, string manipulation, PathBuf/Path, file reading/writing, walkdir, ignore crate, regex, LazyLock, OnceLock, anyhow, error chains, serde JSON/YAML, BTreeMap, struct constructors, result accumulation, impl Into<String>, dyn Write, env vars, exit codes, output formats, iterator chaining, vec operations, integration testing.

Advanced (Examples 58-80): Production CLI

Focus: The patterns that distinguish amateur from professional Rust CLI codebases.

Module organization, type aliases, Clippy configuration, restriction lints, replacing .unwrap(), custom Display/FromStr, markdown/XML parsing, glob patterns, SHA-2 hashing, compiled regex caches, validation orchestration, release profiles, dual crate layout, match guards, recursive validation, chrono dates, avoiding common pitfalls, complex test fixtures, and a final capstone example synthesizing all concepts.

Key topics: Module organization, type aliases, Clippy, restriction lints, replacing unwrap, Display/FromStr, pulldown-cmark, quick-xml, glob, sha2, OnceLock regex cache, validation orchestration, integration testing assertions, testing stderr, release profiles, dual crate, match guards, recursive validation, chrono, common pitfalls, complex fixtures, capstone CLI.

How to Use This Tutorial

Create a Cargo Project

Most beginner examples are single-file. Create one project for all examples:

cargo new rust-cli-examples && cd rust-cli-examples

For examples that use external crates, add to Cargo.toml:

[dependencies]
clap = { version = "4.6.1", features = ["derive"] }
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yml = "0.0.12"
walkdir = "2.5"
ignore = "0.4"
regex = "1.11"
 
[dev-dependencies]
assert_cmd = "2.0"
tempfile = "3.14"
predicates = "3.1"

Read the Annotations

Every code block uses // => comments to show values, outputs, ownership transfers, and error conditions:

let name = String::from("my-tool"); // => name owns heap string "my-tool"
let borrowed: &str = &name;         // => borrowed is a view into name's data
                                    // => name still owns the string
println!("{}", borrowed);           // => Output: my-tool

Follow the Progression

Start with Beginner even if you know other systems languages. Rust's ownership model is unique and requires building intuition from real CLI scenarios. Skipping ahead leaves gaps that cause confusion when the borrow checker rejects code that looks correct.

Five-Part Example Format

Every example uses this structure:

  1. Brief explanation (2-4 sentences): what the concept is and why it matters for CLI tools
  2. Mermaid diagram (when the concept benefits from visualization): ownership flows, module structure, error chains
  3. Heavily annotated code: every significant line has // => comments showing state, values, and outputs
  4. Key Takeaway: the core insight in 1-2 sentences
  5. Why It Matters: production relevance and connection to real CLI codebases

Prerequisites

Required: Experience with at least one of Java, Python, Go, or JavaScript/TypeScript. Ability to run cargo new and cargo run.

Not required: Prior Rust experience, systems programming background, understanding of memory management.

Examples by Level

Beginner (Examples 1-28)

Intermediate (Examples 29-57)

Advanced (Examples 58-80)

Last updated December 29, 2025

Command Palette

Search for a command to run...