DEV Community

Cover image for [Rust Guide] 12.1 Receiving Command-Line Arguments
SomeB1oody
SomeB1oody

Posted on • Edited on • Originally published at someb1oody.github.io

[Rust Guide] 12.1 Receiving Command-Line Arguments

12.1.0 Before We Begin

In Chapter 12, we will build a real project: a command-line program. This program is a grep (Global Regular Expression Print), a tool for global regular-expression search and output. Its job is to search for the specified text in the specified file.

This project has several steps:

12.1.1 Standardizing the Input Format

First we need to define a standard input format for passing arguments. I use the following convention:

cargo run text filename.txt
Enter fullscreen mode Exit fullscreen mode

12.1.2 Reading Command-Line Arguments

After standardizing the input, the next problem is reading command-line arguments.

We need a function from Rust’s standard library: std::env::args(). This function returns an iterator (covered in 13.5. Iterators Pt. 1) that yields a series of values. For an iterator, you can use the collect method to turn that series of values into a collection, such as a Vector.

As discussed in 7.4. Use Pt. 1, when a function is nested inside more than one module, we usually bring its parent module into scope.

Here is the code:

use std::env;

fn main() {
    let args:Vec<String> = env::args().collect();
}
Enter fullscreen mode Exit fullscreen mode

Because collect produces a collection, but Rust cannot infer the element type of that collection, we must explicitly declare args as Vec<String>.

Let’s use dbg! to see what happens:

use std::env;

fn main() {
    let args:Vec<String> = env::args().collect();
    dbg!(args);
}
Enter fullscreen mode Exit fullscreen mode

Output 1, with no arguments:

$ cargo run
   Compiling minigrep v0.1.0 (/tmp/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `target/debug/minigrep`
[src/main.rs:5:2] args = [
    "target/debug/minigrep",
]
Enter fullscreen mode Exit fullscreen mode

Output 2, with arguments:

$ cargo run -- needle haystack
   Compiling minigrep v0.1.0 (/tmp/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/minigrep needle haystack`
[src/main.rs:5:2] args = [
    "target/debug/minigrep",
    "needle",
    "haystack",
]
Enter fullscreen mode Exit fullscreen mode

The -- in the second example is used to separate the arguments for the Cargo command from the arguments passed to the program. It tells Cargo that what follows is not a Cargo option or argument, but arguments that should be passed to the program when it runs. env::args does not read or store it.

You can see that even without arguments, this Vector still has one element: the currently executing binary itself, which in this example is "target/debug/minigrep". So the arguments we actually need begin at the second element of args, that is, at index 1.

Once we know where the needed arguments are stored, we can declare variables to hold them. Declare query to store the text to search for, and filename to store the file name:

let query = &args[1]; 
let filename = &args[2];
Enter fullscreen mode Exit fullscreen mode

This works, but if the user enters too few arguments and the index is out of bounds, Rust will panic and stop the program. Of course, you can also combine match with get:

let query = match args.get(1) {  
    Some(arg) => arg,  
    None => panic!("No query provided"),  
};  
let filename = match args.get(2) {  
    Some(arg) => arg,  
    None => panic!("No file name provided"),  
};
Enter fullscreen mode Exit fullscreen mode

We will use the first approach here.

Then print the two variables so the user can confirm the input:

println!("Searching for {}", query);  
println!("In file {}", filename);
Enter fullscreen mode Exit fullscreen mode

12.1.3 The Full Code

Here is all the code written up to this article:

use std::env;  

fn main() {  
    let args:Vec<String> = env::args().collect();  
    let query = &args[1];  
    let filename = &args[2];

    println!("Searching for {}", query);  
    println!("In file {}", filename);  
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)