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.
What env::args() gives you
Section titled “What env::args() gives you”use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("{args:?}");}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.
The first element is the program name
Section titled “The first element is the program name”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());}3Under cargo run that first item is target/debug/args1. Run the binary directly
and it’s whatever you typed:
./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.
Reading one argument
Section titled “Reading one argument”Indexing panics when the argument is missing:
use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("{}", args[1]);}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"); }}cargo run -q -- Adahello, AdaRun the same program with no arguments and it takes the other branch:
cargo run -qno name givenReading all of them
Section titled “Reading all of them”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}"); }}AdaGraceAlanOne 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.
- How to pass arguments to a Rust program with cargo run — the
--separator, and how arguments reach your program - Back to Building a command-line tool