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