Loading pageā¦
Rust walkthroughs
Loading pageā¦
clap::Arg::required and required_if_eq for conditional argument validation?required enforces unconditional argument presence, while required_if_eq makes an argument conditionally required based on another argument's value. With required(true), the argument must always be provided regardless of context. With required_if_eq("other_arg", "value"), the argument becomes required only when other_arg equals the specified value. This enables sophisticated command-line interfaces where argument requirements depend on user choicesālike requiring --output only when --format is set to file, or requiring --port only when --bind-address is specified. These conditional requirements integrate with clap's help generation, automatically displaying context-aware usage messages that reflect which arguments are required under which conditions.
requireduse clap::{Arg, Command};
fn main() {
let matches = Command::new("myapp")
.arg(
Arg::new("input")
.long("input")
.required(true)
.help("Input file path"),
)
.arg(
Arg::new("output")
.long("output")
.required(true)
.help("Output file path"),
)
.get_matches();
// Both --input and --output MUST be provided
println!("Input: {:?}", matches.get_one::<String>("input"));
println!("Output: {:?}", matches.get_one::<String>("output"));
}
// Running without arguments:
// $ myapp
// error: The following required arguments were not provided:
// --input <input>
// --output <output>required(true) creates unconditional dependencies that must be satisfied regardless of other arguments.
required_if_equse clap::{Arg, Command};
fn main() {
let matches = Command::new("myapp")
.arg(
Arg::new("format")
.long("format")
.value_parser(["json", "xml", "file"])
.default_value("json"),
)
.arg(
Arg::new("output")
.long("output")
.required_if_eq("format", "file")
.help("Output file (required when format=file)"),
)
.get_matches();
// --output is only required when --format=file
// Otherwise it's optional
}
// Running with --format json:
// $ myapp --format json
// OK! No --output required
// Running with --format file:
// $ myapp --format file
// error: The following required arguments were not provided:
// --output <output>required_if_eq creates conditional requirements based on another argument's value.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("server")
.arg(
Arg::new("mode")
.long("mode")
.value_parser(["local", "remote"])
.default_value("local"),
)
.arg(
Arg::new("port")
.long("port")
.required_if_eq("mode", "remote")
.help("Port number (required for remote mode)"),
)
.arg(
Arg::new("host")
.long("host")
.required_if_eq("mode", "remote")
.help("Host address (required for remote mode)"),
)
.arg(
Arg::new("timeout")
.long("timeout")
.required_if_eq("mode", "remote")
.help("Connection timeout (required for remote mode)"),
)
.get_matches();
// Multiple arguments become required when mode=remote
}
// Local mode: only --mode (or default)
// $ server --mode local
// OK - no additional requirements
// Remote mode: --port, --host, --timeout all required
// $ server --mode remote
// error: The following required arguments were not provided:
// --port <port>
// --host <host>
// --timeout <timeout>Multiple arguments can depend on the same condition, creating groups of required arguments.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("database")
// Always required
.arg(
Arg::new("name")
.long("name")
.required(true)
.help("Database name"),
)
// Conditionally required
.arg(
Arg::new("host")
.long("host")
.required_if_eq("type", "remote")
.help("Host address for remote databases"),
)
// Optional
.arg(
Arg::new("type")
.long("type")
.value_parser(["local", "remote"])
.default_value("local"),
)
.get_matches();
// --name is always required
// --host is only required when --type=remote
}
// $ database
// error: --name is required
// $ database --name mydb --type local
// OK - --host not required
// $ database --name mydb --type remote
// error: --host is required when type=remoteArguments can mix unconditional and conditional requirements.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("compiler")
.arg(
Arg::new("input")
.required(true)
.help("Input source file"),
)
.arg(
Arg::new("target")
.long("target")
.value_parser(["native", "wasm", "llvm"])
.default_value("native"),
)
.arg(
Arg::new("output")
.long("output")
.required_if_eq("target", "wasm")
.required_if_eq("target", "llvm")
.help("Output file (required for non-native targets)"),
)
.arg(
Arg::new("optimize")
.long("optimize")
.required_if_eq("target", "llvm")
.help("Optimization level (required for LLVM backend)"),
)
.get_matches();
// Different targets have different requirements:
// native: only input required
// wasm: input + output required
// llvm: input + output + optimize required
}
// $ compiler input.rs --target native
// OK
// $ compiler input.rs --target wasm
// error: --output is required
// $ compiler input.rs --target llvm
// error: --output and --optimize are requiredThe same argument can have multiple required_if_eq conditions with different values.
use clap::{Arg, Command};
fn main() {
// required(true): Unconditional
// - Argument must always be present
// - No context dependency
// - Simple validation
// - Clear error messages
// required_if_eq(arg, value): Conditional
// - Argument required only when arg == value
// - Context-dependent validation
// - More complex error messaging
// - Enables hierarchical argument structures
let cmd = Command::new("example")
.arg(
Arg::new("always")
.long("always")
.required(true), // ALWAYS required
)
.arg(
Arg::new("conditional")
.long("conditional")
.required_if_eq("flag", "enabled"), // Required only when flag=enabled
)
.arg(
Arg::new("flag")
.long("flag")
.value_parser(["enabled", "disabled"])
.default_value("disabled"),
);
// always: Must be provided
// conditional: Only required when flag=enabled
}The semantic difference is about unconditional vs conditional obligation.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("tool")
.arg(
Arg::new("command")
.value_parser(["build", "run", "test"])
.required(true)
.index(1),
)
.arg(
Arg::new("file")
.required_if_eq("command", "build")
.required_if_eq("command", "test")
.index(2)
.help("File argument (required for build and test)"),
)
.arg(
Arg::new("args")
.index(3)
.help("Additional arguments"),
)
.get_matches();
// Positional arguments can also use required_if_eq
// build <file>: file required
// run: no file required
// test <file>: file required
}
// $ tool build
// error: <file> is required for build command
// $ tool run
// OK - no file required
// $ tool test src/main.rs
// OKrequired_if_eq works with positional arguments using index-based references.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("deploy")
.arg(
Arg::new("env")
.long("env")
.value_parser(["dev", "staging", "prod"]),
)
.arg(
Arg::new("region")
.long("region")
.value_parser(["us-east", "us-west", "eu-west"]),
)
.arg(
Arg::new("cert")
.long("cert")
.required_if_eq("env", "prod") // Required for prod
.help("SSL certificate path"),
)
.arg(
Arg::new("backup")
.long("backup")
// Multiple conditions - required if ANY condition is met
.required_if_eq("env", "prod")
.required_if_eq("env", "staging")
.help("Backup configuration"),
)
.get_matches();
// --cert: required when env=prod
// --backup: required when env=prod OR env=staging
}
// Note: Multiple required_if_eq calls create OR logic
// Required if ANY condition matchesMultiple required_if_eq calls on the same argument create OR conditions.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("config")
.arg(
Arg::new("mode")
.long("mode")
.value_parser(["auto", "manual"])
.default_value("auto"),
)
.arg(
Arg::new("config-file")
.long("config-file")
.required_unless_eq("mode", "auto")
.help("Configuration file (required unless mode=auto)"),
)
.arg(
Arg::new("setting")
.long("setting")
.required_if_eq("mode", "manual")
.help("Manual setting (required when mode=manual)"),
)
.get_matches();
// required_if_eq("mode", "manual"): Required when mode=manual
// required_unless_eq("mode", "auto"): Required when mode!=auto
// These are inverse conditions
}
// required_if_eq(A, V): Required when arg A equals value V
// required_unless_eq(A, V): Required when arg A does NOT equal value Vrequired_unless_eq provides the inverse semanticārequire unless a condition is met.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("workflow")
// Level 1: Base option
.arg(
Arg::new("action")
.long("action")
.value_parser(["create", "update", "delete"])
.default_value("create"),
)
// Level 2: Required for specific actions
.arg(
Arg::new("id")
.long("id")
.required_if_eq("action", "update")
.required_if_eq("action", "delete")
.help("Resource ID (required for update/delete)"),
)
// Level 3: Required for update action
.arg(
Arg::new("name")
.long("name")
.required_if_eq("action", "update")
.help("New name (required for update)"),
)
// Level 3: Required for create action
.arg(
Arg::new("template")
.long("template")
.required_if_eq("action", "create")
.help("Template name (required for create)"),
)
.get_matches();
// Creates a tree of conditional requirements:
// create -> requires template
// update -> requires id + name
// delete -> requires id
}
// Different actions have different required argument sets
// This creates command-like behavior with conditional requirementsConditional requirements can create dependency trees for complex CLI structures.
use clap::{Arg, Command};
fn main() {
let cmd = Command::new("example")
.arg(
Arg::new("format")
.long("format")
.value_parser(["json", "yaml", "file"]),
)
.arg(
Arg::new("output")
.long("output")
.required_if_eq("format", "file")
.help("Output path (required when --format=file)"),
);
// Help text automatically reflects conditional requirements
// clap generates usage that shows when arguments are required
// Running --format file without --output:
// error: The following required arguments were not provided:
// --output <output>
//
// USAGE:
// example --format <format> --output <output>
//
// The usage string adapts to show required arguments
}Clap's help generation adapts to show conditional requirements in usage messages.
use clap::{Arg, ArgGroup, Command};
fn main() {
let matches = Command::new("remote")
.arg(
Arg::new("ssh")
.long("ssh")
.help("Use SSH connection"),
)
.arg(
Arg::new("https")
.long("https")
.help("Use HTTPS connection"),
)
.arg(
Arg::new("host")
.long("host")
.required(true)
.help("Remote host address"),
)
.arg(
Arg::new("key")
.long("key")
.required_if_eq("ssh", "true")
.help("SSH key path (required when using SSH)"),
)
.arg(
Arg::new("cert")
.long("cert")
.required_if_eq("https", "true")
.help("Certificate path (required when using HTTPS)"),
)
.group(
ArgGroup::new("protocol")
.args(["ssh", "https"])
.required(true),
)
.get_matches();
// Protocol group is required (ssh OR https)
// --key required when --ssh is used
// --cert required when --https is used
}
// Creates mutually exclusive options with conditional dependenciesCombining ArgGroup with required_if_eq creates sophisticated validation rules.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("processor")
.arg(
Arg::new("mode")
.long("mode")
.value_parser(["encode", "decode"])
.default_value("encode"),
)
.arg(
Arg::new("input")
.required(true)
.help("Input file"),
)
.arg(
Arg::new("key")
.long("key")
.required_if_eq("mode", "encode")
.required_if_eq("mode", "decode")
.help("Encryption key (required for both modes)"),
)
.arg(
Arg::new("output")
.long("output")
.required_if_eq("mode", "encode")
.help("Output file (required for encode)"),
)
.arg(
Arg::new("verify")
.long("verify")
.required_if_eq("mode", "decode")
.help("Verify integrity (required for decode)"),
)
.get_matches();
// Different modes require different argument sets:
// encode: input + key + output
// decode: input + key + verify
let mode = matches.get_one::<String>("mode").unwrap();
match mode.as_str() {
"encode" => {
let input = matches.get_one::<String>("input").unwrap();
let key = matches.get_one::<String>("key").unwrap();
let output = matches.get_one::<String>("output").unwrap();
// Encode logic
}
"decode" => {
let input = matches.get_one::<String>("input").unwrap();
let key = matches.get_one::<String>("key").unwrap();
let verify = matches.get_one::<String>("verify").unwrap();
// Decode logic
}
_ => unreachable!(),
}
}Runtime code can assume conditional requirements are satisfied when they're required.
use clap::{Arg, Command};
fn main() {
let matches = Command::new("git-like")
.subcommand_required(true)
.subcommand(
Command::new("commit")
.arg(
Arg::new("message")
.short('m')
.long("message")
.required(true)
.help("Commit message"),
)
.arg(
Arg::new("amend")
.long("amend")
.help("Amend previous commit"),
)
.arg(
Arg::new("author")
.long("author")
.required_if_eq("amend", "true")
.help("Author name (required when amending)"),
),
)
.subcommand(
Command::new("push")
.arg(
Arg::new("remote")
.required(true)
.help("Remote name"),
)
.arg(
Arg::new("branch")
.required(true)
.help("Branch name"),
)
.arg(
Arg::new("force")
.long("force")
.help("Force push"),
)
.arg(
Arg::new("lease")
.long("lease")
.required_if_eq("force", "true")
.help("Lease token (required when force pushing)"),
),
)
.get_matches();
match matches.subcommand() {
Some(("commit", sub_matches)) => {
let message = sub_matches.get_one::<String>("message").unwrap();
let amend = sub_matches.contains_id("amend");
let author = sub_matches.get_one::<String>("author");
if amend {
// author is guaranteed to be Some when amend is true
println!("Amending with author: {:?}", author);
}
println!("Commit: {}", message);
}
Some(("push", sub_matches)) => {
let remote = sub_matches.get_one::<String>("remote").unwrap();
let branch = sub_matches.get_one::<String>("branch").unwrap();
let force = sub_matches.contains_id("force");
let lease = sub_matches.get_one::<String>("lease");
if force {
// lease is guaranteed to be Some when force is true
println!("Force push with lease: {:?}", lease);
}
println!("Push {} to {}", branch, remote);
}
_ => unreachable!(),
}
}
// Each subcommand has its own conditional requirements
// --author required when --amend is present
// --lease required when --force is presentSubcommands can have their own conditional requirement chains.
Key differences:
| Aspect | required(true) | required_if_eq(arg, value) |
|--------|------------------|------------------------------|
| Obligation | Always required | Conditionally required |
| Context | No context needed | Depends on another arg |
| Error message | "required argument not provided" | "required when X=Y" |
| Use case | Global requirements | Mode-dependent requirements |
When to use each:
// Use required(true) when:
// - Argument is always needed regardless of context
// - No dependencies on other arguments
// - Simple, unconditional validation
.arg(Arg::new("input").required(true))
// Use required_if_eq when:
// - Argument requirement depends on user choices
// - Different modes need different arguments
// - Building command-like subcommand structures
.arg(Arg::new("output").required_if_eq("format", "file"))
// Combine both:
.arg(Arg::new("name").required(true)) // Always required
.arg(Arg::new("port").required_if_eq("mode", "server")) // Conditionally requiredMultiple conditions:
// OR logic: required if ANY condition matches
.arg(Arg::new("backup")
.required_if_eq("env", "prod")
.required_if_eq("env", "staging"))
// AND logic: use required_if_eq_all (if available)
// or nest conditions in your validation logicKey insight: required(true) declares unconditional argument necessity, while required_if_eq(arg, value) creates conditional dependencies that adapt to user input. This enables sophisticated CLI designs where argument requirements reflect semantic relationships between optionsādifferent operational modes can require different argument sets, and the help system automatically communicates these requirements. The validation happens at parse time, producing clear error messages that explain not just that an argument is missing, but the condition under which it's required. For complex CLIs, combining subcommands with conditional requirements creates powerful, user-friendly interfaces that guide users toward correct invocation without overwhelming them with requirements that don't apply to their use case.