Loading pageā¦
Rust walkthroughs
Loading pageā¦
clap::Command::mut_arg enable programmatic argument modification after initial command definition?mut_arg allows you to modify an argument after it has been added to a Command, returning a mutable reference to the Arg so you can chain additional configuration methods. This is essential when you need to conditionally modify arguments based on runtime state, share argument definitions between multiple commands with slight variations, or adjust arguments that were created via derive macros. Without mut_arg, you would need to either duplicate argument definitions or construct arguments entirely at runtime. The method takes an argument ID (name or position) and a closure that receives a mutable reference to the Arg, enabling in-place modification while maintaining the fluent builder pattern.
use clap::{Command, Arg};
fn main() {
// Define a command with an argument
let mut cmd = Command::new("myapp")
.arg(
Arg::new("input")
.help("Input file")
.short('i')
.long("input")
);
// Later, modify the argument programmatically
cmd = cmd.mut_arg("input", |arg| {
arg.required(true)
.value_name("FILE")
});
// The argument is now required with a value name
let matches = cmd.try_get_matches_from(["myapp", "--input", "data.txt"]);
match matches {
Ok(m) => println!("Input: {:?}", m.get_one::<String>("input")),
Err(e) => println!("Error: {}", e),
}
}mut_arg takes an argument ID and a closure that receives a mutable reference to modify the argument.
use clap::{Command, Arg, ArgAction};
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("config")
.help("Configuration file")
.long("config")
)
.arg(
Arg::new("verbose")
.help("Increase verbosity")
.short('v')
.long("verbose")
.action(ArgAction::Count)
)
.arg(
Arg::new("output")
.help("Output directory")
.short('o')
.long("output")
);
// Modify by name (the ID given to Arg::new)
cmd.mut_arg("config", |arg| {
arg.required(false)
.value_name("PATH")
.env("MYAPP_CONFIG")
});
// Modify by position (for positional arguments)
// Position indices start at 1
let mut cmd_with_positional = Command::new("example")
.arg(
Arg::new("file")
.help("File to process")
.index(1)
);
cmd_with_positional.mut_arg("file", |arg| {
arg.required(true)
});
println!("Commands configured with modified arguments");
}Arguments can be modified by their name (string ID) or position for positional arguments.
use clap::{Command, Arg, ArgAction};
use std::env;
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("input")
.help("Input file")
.short('i')
.long("input")
)
.arg(
Arg::new("debug")
.help("Enable debug mode")
.long("debug")
.action(ArgAction::SetTrue)
);
// Conditionally modify based on environment
let is_production = env::var("ENVIRONMENT")
.map(|v| v == "production")
.unwrap_or(false);
if is_production {
cmd.mut_arg("debug", |arg| {
arg.hide(true) // Hide in production
.forbid_empty_values(true)
});
} else {
cmd.mut_arg("debug", |arg| {
arg.visible_alias("dev-mode")
});
}
// Conditionally make input required based on feature
let require_input = env::var("REQUIRE_INPUT").is_ok();
if require_input {
cmd.mut_arg("input", |arg| {
arg.required(true)
});
}
println!("Command configured for: {}",
if is_production { "production" } else { "development" }
);
}Runtime conditions can determine how arguments are configured.
use clap::{Command, Arg, ArgAction};
fn create_base_command() -> Command {
Command::new("myapp")
.arg(
Arg::new("config")
.help("Configuration file")
.short('c')
.long("config")
)
.arg(
Arg::new("verbose")
.help("Verbosity level")
.short('v')
.long("verbose")
.action(ArgAction::Count)
)
}
fn main() {
// Create multiple commands that share common arguments
// but with different modifications
let mut build_cmd = create_base_command()
.name("build")
.about("Build the project")
.arg(
Arg::new("release")
.help("Build in release mode")
.long("release")
.action(ArgAction::SetTrue)
);
// For build, config is optional and verbose affects output
build_cmd.mut_arg("config", |arg| {
arg.required(false)
.help("Build configuration file (optional)")
});
let mut test_cmd = create_base_command()
.name("test")
.about("Run tests")
.arg(
Arg::new("filter")
.help("Test filter pattern")
.long("filter")
);
// For test, config defaults to "test.toml"
test_cmd.mut_arg("config", |arg| {
arg.default_value("test.toml")
.help("Test configuration file")
});
let mut deploy_cmd = create_base_command()
.name("deploy")
.about("Deploy the project")
.arg(
Arg::new("target")
.help("Deployment target")
.long("target")
.required(true)
);
// For deploy, config is required and verbose is hidden
deploy_cmd.mut_arg("config", |arg| {
arg.required(true)
});
deploy_cmd.mut_arg("verbose", |arg| {
arg.hide(true)
});
println!("Commands configured with shared base arguments");
}Common arguments can be shared and customized per-command using mut_arg.
use clap::{Parser, Command, Arg, Args, Subcommand};
#[derive(Parser, Debug)]
#[command(name = "myapp")]
struct Cli {
/// Input file to process
#[arg(short, long)]
input: Option<String>,
/// Enable verbose output
#[arg(short, long)]
verbose: bool,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// Build the project
Build {
/// Build in release mode
#[arg(long)]
release: bool,
},
/// Run tests
Test {
/// Test filter pattern
#[arg(long)]
filter: Option<String>,
},
}
fn main() {
// Get the command from derive macro
let mut cmd = Cli::command();
// Now modify the derived arguments programmatically
cmd.mut_arg("input", |arg| {
arg.required(true)
.value_name("FILE")
.help("Input file to process (required)")
});
cmd.mut_arg("verbose", |arg| {
arg.alias("debug")
.help("Enable verbose output (alias: --debug)")
});
// Modify subcommand arguments
if let Some(build_cmd) = cmd.find_subcommand_mut("build") {
build_cmd.mut_arg("release", |arg| {
arg.help("Build in release mode (optimized)")
});
}
if let Some(test_cmd) = cmd.find_subcommand_mut("test") {
test_cmd.mut_arg("filter", |arg| {
arg.required(true)
.help("Test filter pattern (required)")
});
}
// Use the modified command
let matches = cmd.try_get_matches_from(["myapp", "--input", "file.txt", "build"]);
match matches {
Ok(m) => println!("Matches: {:?}", m.subcommand()),
Err(e) => println!("Error: {}", e),
}
}mut_arg enables modification of arguments defined via derive macros.
use clap::{Command, Arg, ArgAction};
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("input")
.help("Input file")
.short('i')
.long("input")
)
.arg(
Arg::new("output")
.help("Output file")
.short('o')
.long("output")
)
.arg(
Arg::new("format")
.help("Output format")
.long("format")
.value_parser(["json", "yaml", "toml"])
);
// Multiple modifications can be chained
cmd.mut_arg("input", |arg| {
arg.required(true)
})
.mut_arg("output", |arg| {
arg.required(true)
})
.mut_arg("format", |arg| {
arg.default_value("json")
.help("Output format (default: json)")
});
// The builder pattern is maintained
println!("Command configured with chained modifications");
}mut_arg returns &mut Command, allowing chaining of multiple modifications.
use clap::{Command, Arg, ArgAction};
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("input")
.help("Input file")
.short('i')
.long("input")
)
.arg(
Arg::new("output")
.help("Output file")
.short('o')
.long("output")
)
.arg(
Arg::new("stdout")
.help("Output to stdout")
.long("stdout")
.action(ArgAction::SetTrue)
)
.arg(
Arg::new("format")
.help("Output format")
.long("format")
);
// Add validation and relationships after initial definition
cmd.mut_arg("output", |arg| {
// output conflicts with stdout
arg.conflicts_with("stdout")
})
.mut_arg("stdout", |arg| {
// stdout conflicts with output
arg.conflicts_with("output")
})
.mut_arg("format", |arg| {
// format requires either output or stdout
arg.requires("output")
.value_parser(["json", "yaml", "toml"])
});
// Test: format requires output
let result = cmd.clone().try_get_matches_from([
"myapp", "--input", "data.txt", "--format", "json"
]);
match result {
Ok(_) => println!("Unexpected success"),
Err(e) => println!("Expected error: {}", e),
}
// Test: output and stdout conflict
let result = cmd.try_get_matches_from([
"myapp", "--input", "data.txt",
"--output", "out.txt", "--stdout"
]);
match result {
Ok(_) => println!("Unexpected success"),
Err(e) => println!("Expected error: {}", e),
}
}Validation rules and argument relationships can be added via mut_arg.
use clap::{Command, Arg, ArgAction};
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("config")
.help("Configuration file")
.short('c')
.long("config")
.global(true) // Available to all subcommands
)
.arg(
Arg::new("verbose")
.help("Verbosity level")
.short('v')
.long("verbose")
.action(ArgAction::Count)
.global(true)
)
.subcommand(
Command::new("build")
.about("Build the project")
.arg(
Arg::new("release")
.help("Build in release mode")
.long("release")
.action(ArgAction::SetTrue)
)
)
.subcommand(
Command::new("test")
.about("Run tests")
);
// Modify global arguments
cmd.mut_arg("config", |arg| {
arg.env("MYAPP_CONFIG")
.help("Configuration file (env: MYAPP_CONFIG)")
})
.mut_arg("verbose", |arg| {
arg.alias("v")
.help("Verbosity level (can be specified multiple times)")
});
// Global arguments are still accessible via subcommand
let matches = cmd.try_get_matches_from([
"myapp", "-c", "config.toml", "-vv", "build", "--release"
]);
match matches {
Ok(m) => {
println!("Config: {:?}", m.get_one::<String>("config"));
println!("Verbose: {:?}", m.get_count("verbose"));
if let Some(build) = m.subcommand_matches("build") {
println!("Release: {:?}", build.get_flag("release"));
}
}
Err(e) => println!("Error: {}", e),
}
}Global arguments can be modified, and changes apply to all subcommands.
use clap::{Command, Arg, ArgAction};
use std::env;
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("host")
.help("Server host")
.long("host")
)
.arg(
Arg::new("port")
.help("Server port")
.long("port")
)
.arg(
Arg::new("timeout")
.help("Connection timeout in seconds")
.long("timeout")
)
.arg(
Arg::new("retries")
.help("Number of retries")
.long("retries")
);
// Read configuration from environment
let prefix = env::var("MYAPP_PREFIX").unwrap_or_else(|_| "MYAPP".to_string());
// Apply environment variable mappings
cmd.mut_arg("host", |arg| {
arg.env(format!("{}_HOST", prefix))
.default_value("localhost")
})
.mut_arg("port", |arg| {
arg.env(format!("{}_PORT", prefix))
.default_value("8080")
})
.mut_arg("timeout", |arg| {
arg.env(format!("{}_TIMEOUT", prefix))
.default_value("30")
})
.mut_arg("retries", |arg| {
arg.env(format!("{}_RETRIES", prefix))
.default_value("3")
});
// Now arguments can be set via environment variables
env::set_var("MYAPP_HOST", "example.com");
env::set_var("MYAPP_PORT", "443");
let matches = cmd.try_get_matches_from(["myapp"]);
match matches {
Ok(m) => {
println!("Host: {:?}", m.get_one::<String>("host")); // "example.com"
println!("Port: {:?}", m.get_one::<String>("port")); // "443"
println!("Timeout: {:?}", m.get_one::<String>("timeout")); // "30"
println!("Retries: {:?}", m.get_one::<String>("retries")); // "3"
}
Err(e) => println!("Error: {}", e),
}
env::remove_var("MYAPP_HOST");
env::remove_var("MYAPP_PORT");
}Environment variable bindings can be added dynamically based on runtime configuration.
use clap::{Command, Arg, ArgAction};
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("config")
.help("Global config file")
.short('c')
.long("config")
.global(true)
)
.subcommand(
Command::new("server")
.about("Run the server")
.arg(
Arg::new("port")
.help("Server port")
.short('p')
.long("port")
)
.arg(
Arg::new("host")
.help("Server host")
.long("host")
)
.subcommand(
Command::new("start")
.about("Start the server")
.arg(
Arg::new("daemon")
.help("Run as daemon")
.long("daemon")
.action(ArgAction::SetTrue)
)
)
)
.subcommand(
Command::new("client")
.about("Run the client")
.arg(
Arg::new("url")
.help("Server URL")
.long("url")
)
);
// Modify root-level argument
cmd.mut_arg("config", |arg| {
arg.default_value("config.toml")
});
// Modify subcommand arguments
if let Some(server_cmd) = cmd.find_subcommand_mut("server") {
server_cmd.mut_arg("port", |arg| {
arg.default_value("8080")
.required(false)
});
server_cmd.mut_arg("host", |arg| {
arg.default_value("127.0.0.1")
});
// Modify nested subcommand
if let Some(start_cmd) = server_cmd.find_subcommand_mut("start") {
start_cmd.mut_arg("daemon", |arg| {
arg.alias("d")
.help("Run as daemon (alias: -d)")
});
}
}
if let Some(client_cmd) = cmd.find_subcommand_mut("client") {
client_cmd.mut_arg("url", |arg| {
arg.required(true)
.env("MYAPP_URL")
});
}
println!("Command and subcommands configured");
}find_subcommand_mut combined with mut_arg allows modifying nested command structures.
use clap::{Command, Arg, ArgAction, error::ErrorKind};
fn main() {
let mut cmd = Command::new("myapp")
.arg(
Arg::new("input")
.help("Input file")
.short('i')
.long("input")
);
// mut_arg panics if the argument doesn't exist
// To safely handle this, check first with get_arguments
// Safe: argument exists
cmd.mut_arg("input", |arg| {
arg.required(true)
});
// This would panic:
// cmd.mut_arg("nonexistent", |arg| {
// arg.required(true)
// });
// Instead, verify existence first
let arg_names: Vec<&str> = cmd.get_arguments()
.map(|a| a.get_id().as_str())
.collect();
println!("Available arguments: {:?}", arg_names);
if arg_names.contains(&"input") {
cmd.mut_arg("input", |arg| {
arg.help("Input file (required)")
});
}
// Alternative: use try_get_matches_mut which returns error on unknown arg
// Or check with get_arguments() before modification
}mut_arg panics if the argument doesn't exist; check existence first if unsure.
Key capabilities of mut_arg:
| Capability | Description |
|------------|-------------|
| Modify any property | Change help text, default values, requirements, etc. |
| Add relationships | Set conflicts_with, requires, etc. |
| Add environment variables | Bind arguments to environment variables |
| Chain modifications | Multiple mut_arg calls in sequence |
| Work with subcommands | Modify arguments in nested commands |
| Post-derive modification | Modify arguments created by derive macros |
Modification targets:
| Target | How to access |
|--------|---------------|
| Named argument | cmd.mut_arg("name", \|arg\| ...) |
| Positional argument | cmd.mut_arg("name", \|arg\| ...) using the name given to Arg::new |
| Subcommand argument | cmd.find_subcommand_mut("sub").mut_arg("name", ...) |
| Global argument | cmd.mut_arg("name", ...) modifies globally |
Common modification patterns:
cmd.mut_arg("name", |arg| {
arg.required(true) // Make required
.default_value("default") // Set default
.env("ENV_VAR") // Bind environment variable
.conflicts_with("other") // Add conflict
.requires("dependency") // Add requirement
.hide(true) // Hide from help
.alias("alternative") // Add alias
.value_parser(["a", "b", "c"]) // Set allowed values
.help("New help text") // Change help
})Key insight: mut_arg solves the problem of argument modification after initial definition, enabling patterns that would otherwise require duplicating argument definitions or building all arguments at runtime. This is particularly valuable when working with derive macrosā#[derive(Parser)] generates the command structure, but you can then use mut_arg to add runtime-determined configuration like environment variable bindings, conditional requirements, or help text that depends on runtime state. The method integrates seamlessly with clap's builder pattern: it returns &mut Command, allowing chaining of multiple modifications. Combined with find_subcommand_mut, you can navigate and modify arbitrarily deep command hierarchies. The closure-based API keeps the modification code close to the modification site, improving readability compared to extracting arguments into named variables for later modification.