clap
clap (v4) builds typed CLIs with derive macros, shell completions, and helpful --help output. It is the standard for Rust command-line tools.
Recipe
[dependencies]
clap = { version = "4", features = ["derive"] }use clap::Parser;
#[derive(Parser, Debug)]
#[command(name = "ship", about = "Release helper")]
struct Cli {
/// Environment to deploy
#[arg(short, long, default_value = "staging")]
env: String,
/// Dry run only
#[arg(long)]
dry_run: bool,
}
fn main() {
let cli = Cli::parse();
println!("env={} dry_run={}", cli.env, cli.dry_run);
}When to reach for this:
- Any binary with flags, subcommands, or env fallbacks
- Tools shipped to users (see case studies)
- Internal dev CLIs replacing bash scripts
Working Example
use clap::{Parser, Subcommand};
#[derive(Parser)]
struct Cli {
#[command(subcommand)]
cmd: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// List users
List { limit: u32 },
/// Create user
Create { email: String },
}
fn main() {
let cli = Cli::parse();
match cli.cmd {
Commands::List { limit } => println!("limit={limit}"),
Commands::Create { email } => println!("create {email}"),
}
}What this demonstrates:
- Subcommands as enums
- Doc comments become
--helptext - Derive keeps parsing separate from business logic
Deep Dive
Env and Config
#[arg(long, env = "API_URL")]
api_url: String,Combine with figment or manual TOML for layered config.
Exit Codes
use clap::Error;
// clap handles --help and misuse with exit code 2
// return `std::process::ExitCode` from main for app errorsGotchas
- Breaking users on flag renames - scripts fail silently. Fix: keep aliases
#[arg(long, alias = "old")]. - Parsing in library crates - couples API to CLI. Fix: parse in
main, pass structs inward. - Missing
default_value- required flags annoy local dev. Fix: sensible defaults plus env. - Huge flat CLIs - unmaintainable help. Fix: subcommands per domain.
- No
display_order- related flags scatter. Fix: group withnext_help_heading.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
argh | Minimal Google-style flags | Rich subcommands |
Manual std::env::args | One or two args | Anything users run daily |
bpaf | Functional combinator style | Team knows clap |
FAQs
clap 3 vs 4?
New projects use clap 4 derive API. Migration guides cover builder API changes.
Shell completions?
clap_complete generates bash, zsh, fish scripts from your derive struct.
How do I test CLI parsing?
Call Cli::try_parse_from(["bin", "--env", "prod"]) in unit tests.
Multicall binaries?
Use subcommands or multiple bin targets in Cargo.toml.
Related
- Cargo basics - binary targets
- Deployment basics - shipping CLIs
- Crate Publishing Skill
- Essential Crates Best Practices
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.