Skip to content
Select theme

How to read command-line arguments

Short answer:

std::env::args() returns an iterator over the arguments as owned Strings. The first item is the name the program was launched with, not an argument, so your first real argument is the second item. Collect into a Vec, then skip(1) or index from 1.

The examples use a project called args1, so target/debug/args1 is the first line of every output below. Make your own with cargo new args1 and read your binary’s name in its place.

use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
println!("{args:?}");
}
Terminal window
cargo run -q -- Ada Grace
["target/debug/args1", "Ada", "Grace"]

env::args() hands back an iterator, not a Vec; collect() is what builds one, and each argument arrives as an owned String.

Two arguments went in and the iterator produced three items. The extra one sits at args[0]:

use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
println!("{}", args.len());
}
3

Under cargo run that first item is target/debug/args1. Run the binary directly and it’s whatever you typed:

Terminal window
./target/debug/args1 alpha beta
["./target/debug/args1", "alpha", "beta"]

Nothing guarantees it’s a full path, or even a valid one. Never compare it against a literal, and almost always drop it.

Indexing panics when the argument is missing:

args.get(1) returns an Option instead, so the compiler makes you handle both cases:

use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if let Some(name) = args.get(1) {
println!("hello, {name}");
} else {
println!("no name given");
}
}
Terminal window
cargo run -q -- Ada
hello, Ada

Run the same program with no arguments and it takes the other branch:

Terminal window
cargo run -q
no name given

When the program takes a list, a small echo for instance, iterate and skip the program name on the way in:

use std::env;
fn main() {
for arg in env::args().skip(1) {
println!("{arg}");
}
}
Ada
Grace
Alan

One limit of args() itself: it yields String, so the iterator panics when it reaches an argument that is not valid UTF-8, and a filename with a stray byte is enough to trigger it. When the argument is a path, env::args_os() yields OsString and never converts, which is the case the cargo run page walks through in full.

Once the arguments grow flags and subcommands, a hand-written loop stops scaling, and that’s what clap is for.