]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## Accepting Command Line Arguments |
2 | ||
3 | Let’s create a new project with, as always, `cargo new`. We’ll call our project | |
4 | `minigrep` to distinguish it from the `grep` tool that you might already have | |
5 | on your system. | |
6 | ||
7 | ```text | |
8 | $ cargo new minigrep | |
9 | Created binary (application) `minigrep` project | |
10 | $ cd minigrep | |
11 | ``` | |
12 | ||
13 | The first task is to make `minigrep` accept its two command line arguments: the | |
14 | filename and a string to search for. That is, we want to be able to run our | |
15 | program with `cargo run`, a string to search for, and a path to a file to | |
16 | search in, like so: | |
17 | ||
18 | ```text | |
19 | $ cargo run searchstring example-filename.txt | |
20 | ``` | |
21 | ||
22 | Right now, the program generated by `cargo new` cannot process arguments we | |
dc9dc135 | 23 | give it. Some existing libraries on [crates.io](https://crates.io/) can help |
13cf67c4 XL |
24 | with writing a program that accepts command line arguments, but because you’re |
25 | just learning this concept, let’s implement this capability ourselves. | |
26 | ||
27 | ### Reading the Argument Values | |
28 | ||
29 | To enable `minigrep` to read the values of command line arguments we pass to | |
30 | it, we’ll need a function provided in Rust’s standard library, which is | |
9fa01778 | 31 | `std::env::args`. This function returns an iterator of the command line |
48663c56 XL |
32 | arguments that were given to `minigrep`. We’ll cover iterators fully in |
33 | [Chapter 13][ch13]<!-- ignore -->. For now, you only need to know two details | |
34 | about iterators: iterators produce a series of values, and we can call the | |
35 | `collect` method on an iterator to turn it into a collection, such as a vector, | |
36 | containing all the elements the iterator produces. | |
13cf67c4 XL |
37 | |
38 | Use the code in Listing 12-1 to allow your `minigrep` program to read any | |
9fa01778 | 39 | command line arguments passed to it and then collect the values into a vector. |
13cf67c4 XL |
40 | |
41 | <span class="filename">Filename: src/main.rs</span> | |
42 | ||
43 | ```rust | |
74b04a01 | 44 | {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-01/src/main.rs}} |
13cf67c4 XL |
45 | ``` |
46 | ||
47 | <span class="caption">Listing 12-1: Collecting the command line arguments into | |
48 | a vector and printing them</span> | |
49 | ||
50 | First, we bring the `std::env` module into scope with a `use` statement so we | |
51 | can use its `args` function. Notice that the `std::env::args` function is | |
48663c56 XL |
52 | nested in two levels of modules. As we discussed in [Chapter |
53 | 7][ch7-idiomatic-use]<!-- ignore -->, in cases where the desired function is | |
54 | nested in more than one module, it’s conventional to bring the parent module | |
55 | into scope rather than the function. By doing so, we can easily use other | |
56 | functions from `std::env`. It’s also less ambiguous than adding `use | |
57 | std::env::args` and then calling the function with just `args`, because `args` | |
58 | might easily be mistaken for a function that’s defined in the current module. | |
13cf67c4 XL |
59 | |
60 | > ### The `args` Function and Invalid Unicode | |
61 | > | |
62 | > Note that `std::env::args` will panic if any argument contains invalid | |
63 | > Unicode. If your program needs to accept arguments containing invalid | |
64 | > Unicode, use `std::env::args_os` instead. That function returns an iterator | |
65 | > that produces `OsString` values instead of `String` values. We’ve chosen to | |
66 | > use `std::env::args` here for simplicity, because `OsString` values differ | |
67 | > per platform and are more complex to work with than `String` values. | |
68 | ||
69 | On the first line of `main`, we call `env::args`, and we immediately use | |
70 | `collect` to turn the iterator into a vector containing all the values produced | |
71 | by the iterator. We can use the `collect` function to create many kinds of | |
72 | collections, so we explicitly annotate the type of `args` to specify that we | |
73 | want a vector of strings. Although we very rarely need to annotate types in | |
74 | Rust, `collect` is one function you do often need to annotate because Rust | |
75 | isn’t able to infer the kind of collection you want. | |
76 | ||
77 | Finally, we print the vector using the debug formatter, `:?`. Let’s try running | |
78 | the code first with no arguments and then with two arguments: | |
79 | ||
80 | ```text | |
74b04a01 XL |
81 | {{#include ../listings/ch12-an-io-project/listing-12-01/output.txt}} |
82 | ``` | |
13cf67c4 | 83 | |
74b04a01 XL |
84 | ```text |
85 | {{#include ../listings/ch12-an-io-project/output-only-01-with-args/output.txt}} | |
13cf67c4 XL |
86 | ``` |
87 | ||
88 | Notice that the first value in the vector is `"target/debug/minigrep"`, which | |
89 | is the name of our binary. This matches the behavior of the arguments list in | |
90 | C, letting programs use the name by which they were invoked in their execution. | |
91 | It’s often convenient to have access to the program name in case you want to | |
92 | print it in messages or change behavior of the program based on what command | |
93 | line alias was used to invoke the program. But for the purposes of this | |
94 | chapter, we’ll ignore it and save only the two arguments we need. | |
95 | ||
96 | ### Saving the Argument Values in Variables | |
97 | ||
98 | Printing the value of the vector of arguments illustrated that the program is | |
99 | able to access the values specified as command line arguments. Now we need to | |
100 | save the values of the two arguments in variables so we can use the values | |
9fa01778 | 101 | throughout the rest of the program. We do that in Listing 12-2. |
13cf67c4 XL |
102 | |
103 | <span class="filename">Filename: src/main.rs</span> | |
104 | ||
105 | ```rust,should_panic | |
74b04a01 | 106 | {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-02/src/main.rs}} |
13cf67c4 XL |
107 | ``` |
108 | ||
109 | <span class="caption">Listing 12-2: Creating variables to hold the query | |
110 | argument and filename argument</span> | |
111 | ||
112 | As we saw when we printed the vector, the program’s name takes up the first | |
113 | value in the vector at `args[0]`, so we’re starting at index `1`. The first | |
114 | argument `minigrep` takes is the string we’re searching for, so we put a | |
115 | reference to the first argument in the variable `query`. The second argument | |
116 | will be the filename, so we put a reference to the second argument in the | |
117 | variable `filename`. | |
118 | ||
119 | We temporarily print the values of these variables to prove that the code is | |
120 | working as we intend. Let’s run this program again with the arguments `test` | |
121 | and `sample.txt`: | |
122 | ||
123 | ```text | |
74b04a01 | 124 | {{#include ../listings/ch12-an-io-project/listing-12-02/output.txt}} |
13cf67c4 XL |
125 | ``` |
126 | ||
127 | Great, the program is working! The values of the arguments we need are being | |
128 | saved into the right variables. Later we’ll add some error handling to deal | |
129 | with certain potential erroneous situations, such as when the user provides no | |
130 | arguments; for now, we’ll ignore that situation and work on adding file-reading | |
131 | capabilities instead. | |
48663c56 XL |
132 | |
133 | [ch13]: ch13-00-functional-features.html | |
134 | [ch7-idiomatic-use]: ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths |