Rust's `clap`: Build CLIs by Describing Them
`clap` lets you define a Rust struct representing your CLI's arguments, and it generates the parser, help text, and validation. It's used for building any Rust CLI, but its feature-richness can increase binary size over simpler alternatives.
WHY IT EXISTS Manually parsing command-line arguments is tedious and error-prone. Developers have to handle flags, options with values, positional arguments, input validation, and generating helpful error messages and --help text. This is undifferentiated boilerplate that distracts from an application's core logic.
THE MENTAL MODEL Think of clap as a contract. You define a Rust struct that describes the "shape" of your desired command-line arguments. clap then acts as a factory that takes this blueprint and builds a complete, robust parser for you. This includes help generation, version flags, error reporting with suggestions, and even shell completions. You declare what arguments you want, and clap handles how to get them from the user's input.
HOW IT WORKS The most common method is the "derive" API. You create a struct, annotate it with #[derive(Parser)], and then add fields for each argument. You use #[arg(...)] attributes on the fields to specify details like short names (-c), long names (--count), default values, and help text. In your main function, a single call to YourStruct::parse() reads the command-line arguments, populates your struct instance, or exits with a helpful error or help message if the input is invalid.
WHEN TO USE IT Use clap for almost any command-line application in Rust. It's the de-facto standard for building tools that feel professional and robust, providing a polished experience out of the box. It's perfect for applications needing subcommands (like git push), complex validation rules, automatic help text generation, and shell completion scripts.
WHEN NOT TO USE IT The primary trade-off is binary size and compile time. While reasonable for most projects, clap's rich feature set adds overhead. If you are building a tiny utility for an embedded system or a context where every kilobyte of the final binary is critical, a more lightweight parser might be a better choice. For most desktop and server applications, this is not a concern.
ONE CANONICAL EXAMPLE A simple program might define a struct Cli with #[derive(Parser)]. Inside, a field name: String gets an attribute #[arg(short, long)] to accept -n or --name. Another field, count: u8, gets a similar attribute plus a default value: #[arg(short, long, default_value_t = 1)]. In the main function, calling let cli = Cli::parse(); handles all parsing. You can then access cli.name and cli.count as fully-typed Rust values.
Read the original → docs.rs
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.