Step 1 of 6
Command-line arguments
Rust is a favorite for command-line tools (ripgrep, fd, bat, uv and Cargo itself are written in it): they start instantly, ship as one file, and don't crash on odd input. A command-line tool gets its instructions as arguments: the words typed after its name.
$ grep -n error app.log
^^^^^^^^^^^^^^^^^ three arguments: "-n", "error", "app.log"
The shell splits the line into words (quotes keep spaces together: "big world" is one argument) and the program receives them as a list of strings. In Rust that list comes from std::env::args():
use std::env;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
println!("got {} arguments", args.len());
for (i, arg) in args.iter().enumerate() {
println!("{i}: {arg}");
}
}
got 0 arguments
The example above ran without arguments. With Cargo, everything after -- goes to your program (before it, the arguments are for Cargo itself):
$ cargo run -- hello "big world" 42
got 3 arguments
0: hello
1: big world
2: 42
Piece by piece:
env::args()returns an iterator ofStrings. The first item is the program's own path (liketarget/debug/greet), which is why weskip(1).collect()gathers the rest into aVec<String>you can index, count and pass around.- Arguments are always text.
"42"is aString; to use it as a number youparseit, and handle the error when it isn't one. env::args()panics if an argument isn't valid Unicode (rare; possible with odd file names on Linux). Tools that must accept any file name useenv::args_os(), which yieldsOsStrings.
Common mistakes
Indexing without checking. args[1] panics when the user gave no arguments, the most common way a CLI crashes:
fn main() {
let args: Vec<String> = std::env::args().collect();
let name = &args[1];
println!("Hello, {name}!");
}
thread 'main' (4242) panicked at src/main.rs:3:21:
index out of bounds: the len is 1 but the index is 1
Ask for an Option instead and handle the None, usually by printing a usage line that shows how to call the tool:
fn main() {
match std::env::args().nth(1) {
Some(name) => println!("Hello, {name}!"),
None => println!("usage: greet <name>"),
}
}
usage: greet <name>
- Forgetting
skip(1): the program's path shows up as the first "real" argument. - Printing
args[0]in messages: it's a full path that differs between machines. Write the tool's name yourself (usage: greet ...).
Your task
Write greet: print Hello, <name>! for every argument, one per line. With no arguments, print usage: greet <name>.... (On this site the tests run your program with different arguments.)
Challenge 2
Write args: print every argument with its position, starting at 1, as 1: first. Then print a final line total: N. With no arguments it just prints total: 0.
Challenge 3
Write stats: every argument is a number (f64). Print count: N, then min: X and max: Y (min and max with two decimals). Arguments that aren't numbers are skipped, with a line skipping "x" printed for each (in order, before the results). If no numbers are left, print no numbers instead of the three result lines.