Argument Parsing in Rust

Michael Zhao
4 min readFeb 5, 2023

--

When you type git --help on Linux terminal, you will see the prints like:

shell> git --help
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one
...... <omitted>

Git program supports a long list of arguments. We take it an example to talk about what the arguments of a program should be like.

To give a good command line interface to users, the argument handling part of a program should support follow features:

  • Options that takes parameters
  • Flags/Switches that do not take any parameter
  • Both long name (starting with double hyphen like --paginate) and short name (starting with single hyphen like -p)
  • Friendly help message with --help
  • Message for wrong parameters
  • Subcommands (like git add)

Taking Program Arguments in Rust

The Rust Book introduced how to take the arguments to a Rust program by using std::env. To understand what the user has inputted, your main() function may start with:

fn main() {
let args: Vec<String> = std::env::args().collect();
for arg in args {
println!("{arg}");
}
}

Run the program with some random parameters, you will see:

shell > ./target/debug/hello --kind apple --count 42
./target/debug/hello
--kind
apple
--count
42

You can obtain all the input parameters with the form of a list of string. But that is only the beginning. Now you have to handle these strings in following aspects:

  • Parsing the argument names and validating them
  • Parsing the argument values and convert if they are numbers
  • Formatting the help message
  • Giving useful prompt message for any missing argument or any argument error
  • If you want to support subcommand, all the work mentioned above need to be done for each subcommand

The parsing of the input will be a headache if your program support a rich set of argument. It would be a good idea to introduce an external crate in your Rust program to take care of the user input, so you can concentrate on your own logic of the program.

Argh

argh is my favorite argument parsing crate. Because it’s simple to use, and it has a very small memory footprint.

Now I will create a demo program to handle the arguments I have played in the previous command line example. I assume this is a program used to order fruit. I need to tell the program what kind of fruit I need (with --kind parameter) and how many to order (with --count).

Here is all the code I need to write to parse the arguments by leveraging argh:

use argh::FromArgs;

#[derive(FromArgs)]
/// A demo for argument handling
struct Args {
/// set fruit kind
#[argh(option, long = "kind", short = 'k')]
kind: String,

/// set fruit count
#[argh(option, short = 'c', default = "12")]
count: usize,

/// print version information
#[argh(switch, short = 'v')]
version: bool,
}

fn main() {
let args: Args = argh::from_env();
println!("{}, {:x}", args.kind, args.count);
if args.version {
println!("demo v0.0.1");
}
}

Run the code with --help argument, you can see that argh has created a standard user interface for my demo program.

shell > ./target/debug/hello --help
Usage: hello -k <kind> [-c <count>] [-v]

A demo for argument handling

Options:
-k, --kind set fruit kind
-c, --count set fruit count
-v, --version print version information
--help display usage information

With argh you can handle the arguments well in a very easy way:

  • In the annotation #[argh()] you can specify the long name and short name of the argument. The annotated variable will take the value. By default, the variable name itself is used as the long name of the argument if the long keyword is not specified.
  • The argument can have a default value in case the user omit it.
  • Being applied #[argh(switch)], an argument become a flag that doesn’t take any parameter.
  • Help message is created from the comments.
  • If the parameter is numerical, the conversion is done automatically.

argh validates the input, and display error message if there is any problem in the command:

# missing argument
shell >./target/debug/hello
Required options not provided:
--kind

Run hello --help for more information.

# wrong type
shell >./target/debug/hello --kind apple --count a
Error parsing option '--count' with value 'a': invalid digit found in string

Run hello --help for more information.

argh can also handle subcommand. It works in the similar way like this example but is a little bit more complex. I will not show it here. Please read the document if you need.

Alternatives

argh is not the only option to parse the application arguments.

A Github project Rust Arg Parsing Benchmarks compared some well-known argument parsing crates.

The project also provides a guideline for you to balance between these different crates. On the time of write, the page only compares bpaf and clap.

My personal opinion is: clap is the most powerful one, but it is also the biggest one in binary size. If you care the size, and the arguments of your program is not very complex, argh can be a good choice.

--

--