]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## Storing Lists of Values with Vectors |
2 | ||
3 | The first collection type we’ll look at is `Vec<T>`, also known as a *vector*. | |
4 | Vectors allow you to store more than one value in a single data structure that | |
5 | puts all the values next to each other in memory. Vectors can only store values | |
6 | of the same type. They are useful when you have a list of items, such as the | |
7 | lines of text in a file or the prices of items in a shopping cart. | |
8 | ||
9 | ### Creating a New Vector | |
10 | ||
5099ac24 | 11 | To create a new empty vector, we call the `Vec::new` function, as shown in |
69743fb6 | 12 | Listing 8-1. |
13cf67c4 XL |
13 | |
14 | ```rust | |
74b04a01 | 15 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-01/src/main.rs:here}} |
13cf67c4 XL |
16 | ``` |
17 | ||
18 | <span class="caption">Listing 8-1: Creating a new, empty vector to hold values | |
19 | of type `i32`</span> | |
20 | ||
21 | Note that we added a type annotation here. Because we aren’t inserting any | |
22 | values into this vector, Rust doesn’t know what kind of elements we intend to | |
23 | store. This is an important point. Vectors are implemented using generics; | |
24 | we’ll cover how to use generics with your own types in Chapter 10. For now, | |
5099ac24 FG |
25 | know that the `Vec<T>` type provided by the standard library can hold any type. |
26 | When we create a vector to hold a specific type, we can specify the type within | |
13cf67c4 XL |
27 | angle brackets. In Listing 8-1, we’ve told Rust that the `Vec<T>` in `v` will |
28 | hold elements of the `i32` type. | |
29 | ||
5099ac24 FG |
30 | More often, you’ll create a `Vec<T>` with initial values and Rust will infer |
31 | the type of value you want to store, so you rarely need to do this type | |
32 | annotation. Rust conveniently provides the `vec!` macro, which will create a | |
33 | new vector that holds the values you give it. Listing 8-2 creates a new | |
34 | `Vec<i32>` that holds the values `1`, `2`, and `3`. The integer type is `i32` | |
35 | because that’s the default integer type, as we discussed in the [“Data | |
36 | Types”][data-types]<!-- ignore --> section of Chapter 3. | |
13cf67c4 XL |
37 | |
38 | ```rust | |
74b04a01 | 39 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-02/src/main.rs:here}} |
13cf67c4 XL |
40 | ``` |
41 | ||
42 | <span class="caption">Listing 8-2: Creating a new vector containing | |
43 | values</span> | |
44 | ||
45 | Because we’ve given initial `i32` values, Rust can infer that the type of `v` | |
46 | is `Vec<i32>`, and the type annotation isn’t necessary. Next, we’ll look at how | |
47 | to modify a vector. | |
48 | ||
49 | ### Updating a Vector | |
50 | ||
51 | To create a vector and then add elements to it, we can use the `push` method, | |
69743fb6 | 52 | as shown in Listing 8-3. |
13cf67c4 XL |
53 | |
54 | ```rust | |
74b04a01 | 55 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-03/src/main.rs:here}} |
13cf67c4 XL |
56 | ``` |
57 | ||
58 | <span class="caption">Listing 8-3: Using the `push` method to add values to a | |
59 | vector</span> | |
60 | ||
61 | As with any variable, if we want to be able to change its value, we need to | |
62 | make it mutable using the `mut` keyword, as discussed in Chapter 3. The numbers | |
63 | we place inside are all of type `i32`, and Rust infers this from the data, so | |
64 | we don’t need the `Vec<i32>` annotation. | |
65 | ||
13cf67c4 XL |
66 | ### Reading Elements of Vectors |
67 | ||
5099ac24 FG |
68 | There are two ways to reference a value stored in a vector: via indexing or |
69 | using the `get` method. In the following examples, we’ve annotated the types of | |
70 | the values that are returned from these functions for extra clarity. | |
13cf67c4 | 71 | |
923072b8 | 72 | Listing 8-4 shows both methods of accessing a value in a vector, with indexing |
5099ac24 | 73 | syntax and the `get` method. |
13cf67c4 XL |
74 | |
75 | ```rust | |
923072b8 | 76 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-04/src/main.rs:here}} |
13cf67c4 XL |
77 | ``` |
78 | ||
923072b8 | 79 | <span class="caption">Listing 8-4: Using indexing syntax or the `get` method to |
13cf67c4 XL |
80 | access an item in a vector</span> |
81 | ||
923072b8 FG |
82 | Note a few details here. We use the index value of `2` to get the third element |
83 | because vectors are indexed by number, starting at zero. Using `&` and `[]` | |
84 | gives us a reference to the element at the index value. When we use the `get` | |
85 | method with the index passed as an argument, we get an `Option<&T>` that we can | |
86 | use with `match`. | |
13cf67c4 | 87 | |
5099ac24 FG |
88 | The reason Rust provides these two ways to reference an element is so you can |
89 | choose how the program behaves when you try to use an index value outside the | |
90 | range of existing elements. As an example, let’s see what happens when we have | |
91 | a vector of five elements and then we try to access an element at index 100 | |
923072b8 | 92 | with each technique, as shown in Listing 8-5. |
13cf67c4 XL |
93 | |
94 | ```rust,should_panic,panics | |
923072b8 | 95 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-05/src/main.rs:here}} |
13cf67c4 XL |
96 | ``` |
97 | ||
923072b8 | 98 | <span class="caption">Listing 8-5: Attempting to access the element at index |
13cf67c4 XL |
99 | 100 in a vector containing five elements</span> |
100 | ||
101 | When we run this code, the first `[]` method will cause the program to panic | |
102 | because it references a nonexistent element. This method is best used when you | |
103 | want your program to crash if there’s an attempt to access an element past the | |
104 | end of the vector. | |
105 | ||
106 | When the `get` method is passed an index that is outside the vector, it returns | |
107 | `None` without panicking. You would use this method if accessing an element | |
5099ac24 FG |
108 | beyond the range of the vector may happen occasionally under normal |
109 | circumstances. Your code will then have logic to handle having either | |
110 | `Some(&element)` or `None`, as discussed in Chapter 6. For example, the index | |
111 | could be coming from a person entering a number. If they accidentally enter a | |
112 | number that’s too large and the program gets a `None` value, you could tell the | |
113 | user how many items are in the current vector and give them another chance to | |
114 | enter a valid value. That would be more user-friendly than crashing the program | |
115 | due to a typo! | |
13cf67c4 XL |
116 | |
117 | When the program has a valid reference, the borrow checker enforces the | |
118 | ownership and borrowing rules (covered in Chapter 4) to ensure this reference | |
119 | and any other references to the contents of the vector remain valid. Recall the | |
120 | rule that states you can’t have mutable and immutable references in the same | |
923072b8 | 121 | scope. That rule applies in Listing 8-6, where we hold an immutable reference |
5099ac24 FG |
122 | to the first element in a vector and try to add an element to the end. This |
123 | program won’t work if we also try to refer to that element later in the | |
124 | function: | |
13cf67c4 | 125 | |
923072b8 | 126 | |
13cf67c4 | 127 | ```rust,ignore,does_not_compile |
923072b8 | 128 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-06/src/main.rs:here}} |
13cf67c4 XL |
129 | ``` |
130 | ||
923072b8 | 131 | <span class="caption">Listing 8-6: Attempting to add an element to a vector |
13cf67c4 XL |
132 | while holding a reference to an item</span> |
133 | ||
134 | Compiling this code will result in this error: | |
135 | ||
923072b8 | 136 | |
f035d41b | 137 | ```console |
923072b8 | 138 | {{#include ../listings/ch08-common-collections/listing-08-06/output.txt}} |
13cf67c4 XL |
139 | ``` |
140 | ||
923072b8 | 141 | The code in Listing 8-6 might look like it should work: why should a reference |
5099ac24 FG |
142 | to the first element care about changes at the end of the vector? This error is |
143 | due to the way vectors work: because vectors put the values next to each other | |
144 | in memory, adding a new element onto the end of the vector might require | |
145 | allocating new memory and copying the old elements to the new space, if there | |
146 | isn’t enough room to put all the elements next to each other where the vector | |
147 | is currently stored. In that case, the reference to the first element would be | |
148 | pointing to deallocated memory. The borrowing rules prevent programs from | |
149 | ending up in that situation. | |
13cf67c4 | 150 | |
74b04a01 XL |
151 | > Note: For more on the implementation details of the `Vec<T>` type, see [“The |
152 | > Rustonomicon”][nomicon]. | |
13cf67c4 | 153 | |
5099ac24 | 154 | ### Iterating over the Values in a Vector |
13cf67c4 | 155 | |
5099ac24 | 156 | To access each element in a vector in turn, we would iterate through all of the |
923072b8 | 157 | elements rather than use indices to access one at a time. Listing 8-7 shows how |
5099ac24 FG |
158 | to use a `for` loop to get immutable references to each element in a vector of |
159 | `i32` values and print them. | |
13cf67c4 XL |
160 | |
161 | ```rust | |
923072b8 | 162 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-07/src/main.rs:here}} |
13cf67c4 XL |
163 | ``` |
164 | ||
923072b8 | 165 | <span class="caption">Listing 8-7: Printing each element in a vector by |
13cf67c4 XL |
166 | iterating over the elements using a `for` loop</span> |
167 | ||
168 | We can also iterate over mutable references to each element in a mutable vector | |
923072b8 | 169 | in order to make changes to all the elements. The `for` loop in Listing 8-8 |
69743fb6 | 170 | will add `50` to each element. |
13cf67c4 XL |
171 | |
172 | ```rust | |
923072b8 | 173 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-08/src/main.rs:here}} |
13cf67c4 XL |
174 | ``` |
175 | ||
923072b8 | 176 | <span class="caption">Listing 8-8: Iterating over mutable references to |
13cf67c4 XL |
177 | elements in a vector</span> |
178 | ||
179 | To change the value that the mutable reference refers to, we have to use the | |
923072b8 FG |
180 | `*` dereference operator to get to the value in `i` before we can use the `+=` |
181 | operator. We’ll talk more about the dereference operator in the [“Following the | |
182 | Pointer to the Value with the Dereference Operator”][deref]<!-- ignore --> | |
9fa01778 | 183 | section of Chapter 15. |
13cf67c4 | 184 | |
923072b8 FG |
185 | Iterating over a vector, whether immutably or mutably, is safe because of the |
186 | borrow checker's rules. If we attempted to insert or remove items in the `for` | |
187 | loop bodies in Listing 8-7 and Listing 8-8, we would get a compiler error | |
188 | similar to the one we got with the code in Listing 8-6. The reference to the | |
189 | vector that the `for` loop holds prevents simultaneous modification of the | |
190 | whole vector. | |
191 | ||
13cf67c4 XL |
192 | ### Using an Enum to Store Multiple Types |
193 | ||
5099ac24 FG |
194 | Vectors can only store values that are the same type. This can be inconvenient; |
195 | there are definitely use cases for needing to store a list of items of | |
196 | different types. Fortunately, the variants of an enum are defined under the | |
197 | same enum type, so when we need one type to represent elements of different | |
198 | types, we can define and use an enum! | |
13cf67c4 XL |
199 | |
200 | For example, say we want to get values from a row in a spreadsheet in which | |
201 | some of the columns in the row contain integers, some floating-point numbers, | |
202 | and some strings. We can define an enum whose variants will hold the different | |
5099ac24 FG |
203 | value types, and all the enum variants will be considered the same type: that |
204 | of the enum. Then we can create a vector to hold that enum and so, ultimately, | |
923072b8 | 205 | holds different types. We’ve demonstrated this in Listing 8-9. |
13cf67c4 XL |
206 | |
207 | ```rust | |
923072b8 | 208 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-09/src/main.rs:here}} |
13cf67c4 XL |
209 | ``` |
210 | ||
923072b8 | 211 | <span class="caption">Listing 8-9: Defining an `enum` to store values of |
13cf67c4 XL |
212 | different types in one vector</span> |
213 | ||
214 | Rust needs to know what types will be in the vector at compile time so it knows | |
5099ac24 FG |
215 | exactly how much memory on the heap will be needed to store each element. We |
216 | must also be explicit about what types are allowed in this vector. If Rust | |
217 | allowed a vector to hold any type, there would be a chance that one or more of | |
218 | the types would cause errors with the operations performed on the elements of | |
219 | the vector. Using an enum plus a `match` expression means that Rust will ensure | |
220 | at compile time that every possible case is handled, as discussed in Chapter 6. | |
221 | ||
222 | If you don’t know the exhaustive set of types a program will get at runtime to | |
223 | store in a vector, the enum technique won’t work. Instead, you can use a trait | |
224 | object, which we’ll cover in Chapter 17. | |
13cf67c4 XL |
225 | |
226 | Now that we’ve discussed some of the most common ways to use vectors, be sure | |
3c0e092e XL |
227 | to review [the API documentation][vec-api]<!-- ignore --> for all the many |
228 | useful methods defined on `Vec<T>` by the standard library. For example, in | |
923072b8 FG |
229 | addition to `push`, a `pop` method removes and returns the last element. |
230 | ||
231 | ### Dropping a Vector Drops Its Elements | |
232 | ||
233 | Like any other `struct`, a vector is freed when it goes out of scope, as | |
234 | annotated in Listing 8-10. | |
235 | ||
236 | ```rust | |
237 | {{#rustdoc_include ../listings/ch08-common-collections/listing-08-10/src/main.rs:here}} | |
238 | ``` | |
239 | ||
240 | <span class="caption">Listing 8-10: Showing where the vector and its elements | |
241 | are dropped</span> | |
242 | ||
243 | When the vector gets dropped, all of its contents are also dropped, meaning the | |
244 | integers it holds will be cleaned up. The borrow checker ensures that any | |
245 | references to contents of a vector are only used while the vector itself is | |
246 | valid. | |
247 | ||
248 | Let’s move on to the next collection type: `String`! | |
9fa01778 | 249 | |
74b04a01 | 250 | [data-types]: ch03-02-data-types.html#data-types |
3c0e092e | 251 | [nomicon]: ../nomicon/vec/vec.html |
3dfed10e | 252 | [vec-api]: ../std/vec/struct.Vec.html |
9fa01778 | 253 | [deref]: ch15-02-deref.html#following-the-pointer-to-the-value-with-the-dereference-operator |