Arguments, options, and flags
Parse Rust command-line arguments with clap derive. Examples cover flags, defaults, optional values, typed validation, lists, and help text.
Start here: your first CLI#
A command line interface reads words after the program name and turns them into typed Rust values.
Add clap to a binary crate. The derive feature enables #[derive(Parser)]. The extra env feature is used later for environment variables.
cargo new shelf
cd shelf
cargo add clap --features derive,envuse clap::Parser;
/// Print a greeting.
#[derive(Debug, Parser)]
#[command(version)]
struct Cli {
/// Person to greet.
name: String,
/// Repeat the greeting this many times.
#[arg(short, long, default_value_t = 1)]
count: u8,
/// Use a shorter greeting.
#[arg(short, long)]
brief: bool,
}
fn main() {
let cli = Cli::parse();
for _ in 0..cli.count {
if cli.brief {
println!("Hi, {}!", cli.name);
} else {
println!("Hello, {}!", cli.name);
}
}
}cargo run -- Ada -c 2
# Hello, Ada!
# Hello, Ada!
cargo run -- Ada --brief
# Hi, Ada!
cargo run -- --helpcargo run -- tells Cargo to pass the remaining words to your program. Run the compiled binary directly without that separator.
Arguments, options, and flags#
Each field in the parser describes one piece of input. The field type determines what Rust value you receive.
| CLI form | Example | Rust field | Meaning |
|---|---|---|---|
| Positional | shelf book.txt | input: PathBuf | A value in a particular position. |
| Option | shelf --output out.txt | #[arg(long)] output: PathBuf | A named setting that takes a value. |
| Flag | shelf --dry-run | #[arg(long)] dry_run: bool | A named on/off switch; false when absent. |
| Short option | shelf -o out.txt | #[arg(short)] output: PathBuf | A one-letter spelling. |
Field names such as dry_run become --dry-run. #[arg(short, long)] exposes both -o and --output. A plain String positional is required unless you give it a default or wrap it in Option.
use std::path::PathBuf;
use clap::Parser;
#[derive(Parser)]
struct Cli {
input: PathBuf,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(long)]
dry_run: bool,
}
fn main() {
let args = Cli::parse();
println!("input={:?} output={:?} dry_run={}", args.input, args.output, args.dry_run);
}cargo run -- notes.txt -o result.txt --dry-run
# input="notes.txt" output=Some("result.txt") dry_run=trueHelp, version, and discoverability#
Make every command understandable without reading its source.
A /// comment on a parser or field becomes help text. #[command(version)] reads the crate version from Cargo metadata and enables -V/--version. Clap supplies -h/--help. Write usage-oriented descriptions: what a flag changes and what a value means.
use std::path::PathBuf;
use clap::Parser;
/// Search text files for a pattern.
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
/// Text to look for.
pattern: String,
/// File to search.
file: PathBuf,
/// Match without caring about letter case.
#[arg(short, long)]
ignore_case: bool,
}
fn main() {
let _ = Cli::parse();
}cargo run -- --help
cargo run -- --version
cargo run -- needle ./notes.txt --ignore-caseWhen you provide longer doc comments with paragraphs, -h and --help can show different amounts of detail. Use long_about = None when you want the same short summary in both.
Required values, defaults, and optional input#
Use Rust types to express whether a user must supply a value.
use clap::Parser;
#[derive(Parser, Debug)]
struct Cli {
/// Required positional.
input: String,
/// Optional named value.
#[arg(long)]
output: Option<String>,
/// Optional named value with a typed default.
#[arg(long, default_value_t = 10)]
limit: usize,
/// Named switch, false when absent.
#[arg(long)]
quiet: bool,
}
fn main() {
println!("{:#?}", Cli::parse());
}A required --token can be written as #[arg(long)] token: String; omitting it is a parse error. Use Option<T> to permit absence. default_value_t needs a value that can be displayed and parsed back into its type.
cargo run -- input.txt --limit 25
cargo run -- # error: required <INPUT> is missing
cargo run -- input.txt --quietThe uncommon Option<Option<T>> accepts a flag with an optional value; decide what a bare flag means with default_missing_value when appropriate. A simple Option<T> still needs a value after the flag.
Typed values and validation#
Let clap reject malformed input before your command does any work.
Numeric fields, paths, IP addresses, and other supported types are parsed for you. For a finite set of values, derive ValueEnum. For numeric bounds, use value_parser!. A custom type can implement FromStr and return a useful error message.
use clap::{Parser, ValueEnum};
#[derive(Clone, Debug, ValueEnum)]
enum Format {
Text,
Json,
}
#[derive(Parser, Debug)]
struct Cli {
#[arg(long, value_enum, default_value_t = Format::Text)]
format: Format,
#[arg(long, value_parser = clap::value_parser!(u16).range(1..=65535))]
port: u16,
}
impl std::fmt::Display for Format {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self { Self::Text => "text", Self::Json => "json" })
}
}
fn main() {
println!("{:?}", Cli::parse());
}cargo run -- --port 8080 --format json
cargo run -- --port 0 # clap rejects an out-of-range port
cargo run -- --port 8080 --format xml # clap lists the allowed valuesA typed default for ValueEnum must display to a value the enum parser accepts. A simpler alternative is #[arg(value_enum, default_value = "text")] with no Display implementation.
Custom validation for domain values#
Reject an invalid value with a message that tells the user what to fix.
For rules beyond a built-in number range, supply a parsing function. It receives the supplied string and returns either the typed value or an error message. Clap prints that message with its own usage hint.
use clap::Parser;
fn parse_percentage(raw: &str) -> Result<u8, String> {
let value: u8 = raw.parse().map_err(|_| "expected a whole number".to_owned())?;
if value <= 100 { Ok(value) }
else { Err("percentage must be at most 100".to_owned()) }
}
#[derive(Parser)]
struct Cli {
#[arg(long, value_parser = parse_percentage)]
progress: u8,
}
fn main() {
println!("{}%", Cli::parse().progress);
}cargo run -- --progress 75 # 75%
cargo run -- --progress 120 # percentage must be at most 100
cargo run -- --progress many # expected a whole numberValidate the shape and meaning of individual arguments in the parser. Rules that require reading a file or calling a service belong in the work phase and should return a normal application error.
Lists, repeated options, and counters#
Collect several values without writing your own argument loop.
use clap::{ArgAction, Parser};
#[derive(Debug, Parser)]
struct Cli {
/// Each -v increases verbosity.
#[arg(short, long, action = ArgAction::Count)]
verbose: u8,
/// Supply more than once: --tag red --tag blue.
#[arg(long)]
tag: Vec<String>,
/// One flag with comma-separated fields.
#[arg(long, value_delimiter = ',')]
columns: Vec<String>,
/// One or more input files.
#[arg(required = true, num_args = 1..)]
files: Vec<String>,
}
fn main() {
println!("{:#?}", Cli::parse());
}cargo run -- -vv --tag red --tag blue --columns name,size a.txt b.txt
# verbose: 2
# tag: ["red", "blue"]
# columns: ["name", "size"]
# files: ["a.txt", "b.txt"]A Vec<T> on a named option normally collects repeated occurrences. num_args changes values per occurrence; value_delimiter splits one supplied value. They solve different problems.
Help improve CodeRef
Allow Google Analytics to measure page visits and use of the guides? It uses cookies after you accept. Your choice won’t affect access to the site. Privacy notice