143 lines
8.4 KiB
HTML
143 lines
8.4 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Accepting Command Line Arguments</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="accepting-command-line-arguments"><a class="header" href="#accepting-command-line-arguments">Accepting Command Line Arguments</a></h2>
|
||
<p>Let’s create a new project with, as always, <code>cargo new</code>. We’ll call our project
|
||
<code>minigrep</code> to distinguish it from the <code>grep</code> tool that you might already have
|
||
on your system:</p>
|
||
<pre><code class="language-console">$ cargo new minigrep
|
||
Created binary (application) `minigrep` project
|
||
$ cd minigrep
|
||
</code></pre>
|
||
<p>The first task is to make <code>minigrep</code> accept its two command line arguments: the
|
||
file path and a string to search for. That is, we want to be able to run our
|
||
program with <code>cargo run</code>, two hyphens to indicate the following arguments are
|
||
for our program rather than for <code>cargo</code>, a string to search for, and a path to
|
||
a file to search in, like so:</p>
|
||
<pre><code class="language-console">$ cargo run -- searchstring example-filename.txt
|
||
</code></pre>
|
||
<p>Right now, the program generated by <code>cargo new</code> cannot process arguments we
|
||
give it. Some existing libraries on <a href="https://crates.io/">crates.io</a> can help
|
||
with writing a program that accepts command line arguments, but because you’re
|
||
just learning this concept, let’s implement this capability ourselves.</p>
|
||
<h3 id="reading-the-argument-values"><a class="header" href="#reading-the-argument-values">Reading the Argument Values</a></h3>
|
||
<p>To enable <code>minigrep</code> to read the values of command line arguments we pass to
|
||
it, we’ll need the <code>std::env::args</code> function provided in Rust’s standard
|
||
library. This function returns an iterator of the command line arguments passed
|
||
to <code>minigrep</code>. We’ll cover iterators fully in <a href="../ch13/ch13-00-functional-features.html">Chapter 13</a><!-- ignore
|
||
-->. For now, you only need to know two details about iterators: Iterators
|
||
produce a series of values, and we can call the <code>collect</code> method on an iterator
|
||
to turn it into a collection, such as a vector, which contains all the elements
|
||
the iterator produces.</p>
|
||
<p>The code in Listing 12-1 allows your <code>minigrep</code> program to read any command
|
||
line arguments passed to it and then collect the values into a vector.</p>
|
||
<figure class="listing" id="listing-12-1">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">use std::env;
|
||
|
||
fn main() {
|
||
let args: Vec<String> = env::args().collect();
|
||
dbg!(args);
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-12-1">Listing 12-1</a>: Collecting the command line arguments into a vector and printing them</figcaption>
|
||
</figure>
|
||
<p>First, we bring the <code>std::env</code> module into scope with a <code>use</code> statement so that
|
||
we can use its <code>args</code> function. Notice that the <code>std::env::args</code> function is
|
||
nested in two levels of modules. As we discussed in <a href="../ch07/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths">Chapter
|
||
7</a><!-- ignore -->, in cases where the desired function is
|
||
nested in more than one module, we’ve chosen to bring the parent module into
|
||
scope rather than the function. By doing so, we can easily use other functions
|
||
from <code>std::env</code>. It’s also less ambiguous than adding <code>use std::env::args</code> and
|
||
then calling the function with just <code>args</code>, because <code>args</code> might easily be
|
||
mistaken for a function that’s defined in the current module.</p>
|
||
<section class="note" aria-role="note">
|
||
<h3 id="the-args-function-and-invalid-unicode"><a class="header" href="#the-args-function-and-invalid-unicode">The <code>args</code> Function and Invalid Unicode</a></h3>
|
||
<p>Note that <code>std::env::args</code> will panic if any argument contains invalid
|
||
Unicode. If your program needs to accept arguments containing invalid
|
||
Unicode, use <code>std::env::args_os</code> instead. That function returns an iterator
|
||
that produces <code>OsString</code> values instead of <code>String</code> values. We’ve chosen to
|
||
use <code>std::env::args</code> here for simplicity because <code>OsString</code> values differ per
|
||
platform and are more complex to work with than <code>String</code> values.</p>
|
||
</section>
|
||
<p>On the first line of <code>main</code>, we call <code>env::args</code>, and we immediately use
|
||
<code>collect</code> to turn the iterator into a vector containing all the values produced
|
||
by the iterator. We can use the <code>collect</code> function to create many kinds of
|
||
collections, so we explicitly annotate the type of <code>args</code> to specify that we
|
||
want a vector of strings. Although you very rarely need to annotate types in
|
||
Rust, <code>collect</code> is one function you do often need to annotate because Rust
|
||
isn’t able to infer the kind of collection you want.</p>
|
||
<p>Finally, we print the vector using the debug macro. Let’s try running the code
|
||
first with no arguments and then with two arguments:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling minigrep v0.1.0 (file:///projects/minigrep)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
|
||
Running `target/debug/minigrep`
|
||
[src/main.rs:5:5] args = [
|
||
"target/debug/minigrep",
|
||
]
|
||
</code></pre>
|
||
<pre><code class="language-console">$ cargo run -- needle haystack
|
||
Compiling minigrep v0.1.0 (file:///projects/minigrep)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.57s
|
||
Running `target/debug/minigrep needle haystack`
|
||
[src/main.rs:5:5] args = [
|
||
"target/debug/minigrep",
|
||
"needle",
|
||
"haystack",
|
||
]
|
||
</code></pre>
|
||
<p>Notice that the first value in the vector is <code>"target/debug/minigrep"</code>, which
|
||
is the name of our binary. This matches the behavior of the arguments list in
|
||
C, letting programs use the name by which they were invoked in their execution.
|
||
It’s often convenient to have access to the program name in case you want to
|
||
print it in messages or change the behavior of the program based on what
|
||
command line alias was used to invoke the program. But for the purposes of this
|
||
chapter, we’ll ignore it and save only the two arguments we need.</p>
|
||
<h3 id="saving-the-argument-values-in-variables"><a class="header" href="#saving-the-argument-values-in-variables">Saving the Argument Values in Variables</a></h3>
|
||
<p>The program is currently able to access the values specified as command line
|
||
arguments. Now we need to save the values of the two arguments in variables so
|
||
that we can use the values throughout the rest of the program. We do that in
|
||
Listing 12-2.</p>
|
||
<figure class="listing" id="listing-12-2">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre><code class="language-rust should_panic noplayground">use std::env;
|
||
|
||
fn main() {
|
||
let args: Vec<String> = env::args().collect();
|
||
|
||
let query = &args[1];
|
||
let file_path = &args[2];
|
||
|
||
println!("Searching for {query}");
|
||
println!("In file {file_path}");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-12-2">Listing 12-2</a>: Creating variables to hold the query argument and file path argument</figcaption>
|
||
</figure>
|
||
<p>As we saw when we printed the vector, the program’s name takes up the first
|
||
value in the vector at <code>args[0]</code>, so we’re starting arguments at index 1. The
|
||
first argument <code>minigrep</code> takes is the string we’re searching for, so we put a
|
||
reference to the first argument in the variable <code>query</code>. The second argument
|
||
will be the file path, so we put a reference to the second argument in the
|
||
variable <code>file_path</code>.</p>
|
||
<p>We temporarily print the values of these variables to prove that the code is
|
||
working as we intend. Let’s run this program again with the arguments <code>test</code>
|
||
and <code>sample.txt</code>:</p>
|
||
<pre><code class="language-console">$ cargo run -- test sample.txt
|
||
Compiling minigrep v0.1.0 (file:///projects/minigrep)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0s
|
||
Running `target/debug/minigrep test sample.txt`
|
||
Searching for test
|
||
In file sample.txt
|
||
</code></pre>
|
||
<p>Great, the program is working! The values of the arguments we need are being
|
||
saved into the right variables. Later we’ll add some error handling to deal
|
||
with certain potential erroneous situations, such as when the user provides no
|
||
arguments; for now, we’ll ignore that situation and work on adding file-reading
|
||
capabilities instead.</p>
|
||
</body>
|
||
</html>
|