mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-01-23 23:50:25 +08:00
Edits to Vec - be more specific, update error messages
This commit is contained in:
parent
f80bccaa64
commit
a417244d98
@ -1,8 +1,8 @@
|
||||
# Vectors
|
||||
|
||||
The first type we'll look at is `Vec<T>`, also known as a *vector*. Vectors
|
||||
allow you to store more than one value in a single data structure next to each
|
||||
other in memory.
|
||||
allow you to store more than one value in a single data structure that puts all
|
||||
the values next to each other in memory.
|
||||
|
||||
## Creating a new vector
|
||||
|
||||
@ -16,12 +16,13 @@ You'll note that we added a type annotation here. Because we don't actually do
|
||||
anything with the vector, Rust doesn't know what kind of elements we intend to
|
||||
store. This is an important point. Vectors are homogenous: they may store many
|
||||
values, but those values must all be the same type. Vectors are generic over
|
||||
the type you store inside them, and the angle brackets here tell Rust that
|
||||
this vector will hold elements of the `i32` type.
|
||||
the type you store inside them (we'll talk about Generics more throroughly in
|
||||
Chapter XX), and the angle brackets here tell Rust that this vector will hold
|
||||
elements of the `i32` type.
|
||||
|
||||
That said, in real code, you very rarely need to do this type annotation. Let's
|
||||
insert some values to see this in action. To put elements in the vector, we can
|
||||
use the `push` method:
|
||||
That said, in real code, you very rarely need to do this type annotation since
|
||||
Rust can infer the type of value we want to store once we insert values. Let's
|
||||
see this in action. To put elements in the vector, we can use the `push` method:
|
||||
|
||||
```rust
|
||||
let mut v = Vec::new();
|
||||
@ -32,9 +33,11 @@ v.push(7);
|
||||
v.push(8);
|
||||
```
|
||||
|
||||
Since these numbers are `i32`s, Rust can infer the type of the vector, so we
|
||||
don't need the annotation. That said, creating a vector with some initial
|
||||
values like this is very common, and so there's a macro to do it for us:
|
||||
Since these numbers are `i32`s, Rust infers the type of data we want to store
|
||||
in the vector, so we don't need the `<i32>` annotation.
|
||||
|
||||
We can improve this code even further. Creating a vector with some initial
|
||||
values like this is very common, so there's a macro to do it for us:
|
||||
|
||||
```rust
|
||||
let v = vec![5, 6, 7, 8];
|
||||
@ -57,29 +60,32 @@ Like any other `struct`, a vector will be freed when it goes out of scope:
|
||||
```
|
||||
|
||||
When the vector gets dropped, it will also drop all of its contents, so those
|
||||
integers are going to be taken care of as well. This may seem like a
|
||||
straightforward point now, but can get a little more complicated once we start
|
||||
to introduce references to the elements of the vector. Let's tackle that next!
|
||||
integers are going to be cleaned up as well. This may seem like a
|
||||
straightforward point, but can get a little more complicated once we start to
|
||||
introduce references to the elements of the vector. Let's tackle that next!
|
||||
|
||||
## Reading elements of vectors
|
||||
|
||||
Now that we know how to make vectors, knowing how to read their contents is a
|
||||
good next step. There are two ways to reference a value stored in a vector.
|
||||
We've annotated the types of the values that are returned from these functions
|
||||
for extra clarity:
|
||||
Now that we know how creating and destroying vectors works, knowing how to read
|
||||
their contents is a good next step. There are two ways to reference a value
|
||||
stored in a vector. In the following examples of these two ways, we've
|
||||
annotated the types of the values that are returned from these functions for
|
||||
extra clarity:
|
||||
|
||||
```rust
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
let three: &i32 = &v[2];
|
||||
let three: Option<&i32> = v.get(2);
|
||||
let third: &i32 = &v[2];
|
||||
let third: Option<&i32> = v.get(2);
|
||||
```
|
||||
|
||||
First, note that we use `2` to get the third element: vectors are indexed by
|
||||
number, starting at zero. Secondly, we have two different ways to do this:
|
||||
using `&` and `[]`s and using a method, `get()`. The square brackets give us a
|
||||
reference, and `get()` gives us an `Option<&T>`. Why two ways? Well, what
|
||||
happens if we tried to do this:
|
||||
First, note that we use the index value of `2` to get the third element:
|
||||
vectors are indexed by number, starting at zero. Secondly, the two different
|
||||
ways to get the third element are using `&` and `[]`s and using the `get`
|
||||
method. The square brackets give us a reference, and `get` gives us an
|
||||
`Option<&T>`. The reason we have two ways to reference an element is so that we
|
||||
can choose the behavior we'd like to have if we try to use an index value that
|
||||
the vector doesn't have an element for:
|
||||
|
||||
```rust,should_panic
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
@ -89,12 +95,17 @@ let does_not_exist = v.get(100);
|
||||
```
|
||||
|
||||
With the `[]`s, Rust will cause a `panic!`. With the `get` method, it will
|
||||
instead return `None` without `panic!`ing. Remember that while `panic!`s will
|
||||
cause your program to stop executing, they do not cause memory unsafety.
|
||||
instead return `None` without `panic!`ing. Deciding which way to access
|
||||
elements in a vector depends on whether you consider an attempted access past
|
||||
the end of the vector to be an error, in which case you would want the `panic!`
|
||||
behavior, or whether this will happen occasionally under normal circumstances
|
||||
and your code will have logic to handle getting `Some(&element)` or `None`.
|
||||
|
||||
The borrow checker remains vigilant about references to the contents of the
|
||||
vector and will make sure that everything stays valid. For example, here's
|
||||
a case that looks okay, but actually isn't:
|
||||
Once we have a valid reference, the borrow checker will enforce the ownership
|
||||
and borrowing rules we covered in Chapter 4 in order to ensure this and other
|
||||
references to the contents of the vector stay valid. For example, here's code
|
||||
that looks like it should be allowed, but it won't compile because the
|
||||
references actually aren't valid anymore:
|
||||
|
||||
```rust,ignore
|
||||
let mut v = vec![1, 2, 3, 4, 5];
|
||||
@ -104,22 +115,33 @@ let first = &v[0];
|
||||
v.push(6);
|
||||
```
|
||||
|
||||
This will give us this error:
|
||||
Compiling this will give us this error:
|
||||
|
||||
```text
|
||||
error: cannot borrow `v` as mutable because it is also borrowed as immutable [E0502]
|
||||
v.push(6);
|
||||
^
|
||||
note: immutable borrow occurs here
|
||||
let first = &v[0];
|
||||
^
|
||||
note: immutable borrow ends here
|
||||
}
|
||||
^
|
||||
error: aborting due to previous error(s)
|
||||
```bash
|
||||
error: cannot borrow `v` as mutable because it is also borrowed as immutable
|
||||
[--explain E0502]
|
||||
|>
|
||||
5 |> let first = &v[0];
|
||||
|> - immutable borrow occurs here
|
||||
7 |> v.push(6);
|
||||
|> ^ mutable borrow occurs here
|
||||
9 |> }
|
||||
|> - immutable borrow ends here
|
||||
```
|
||||
|
||||
Why is this an error? Due to the way vectors work, adding a new element onto
|
||||
the end might require allocating new memory and copying the old elements over.
|
||||
If this happened, our reference would be pointing to deallocated memory. For
|
||||
more on this, see the Nomicon: https://doc.rust-lang.org/stable/nomicon/vec.html
|
||||
This violates one of the ownership rules we covered in Chapter 4: the `push`
|
||||
method needs to have a mutable borrow to the `Vec`, and we aren't allowed to
|
||||
have any immutable borrows while we have a mutable borrow.
|
||||
|
||||
Why is it an error to have a reference to the first element in a vector while
|
||||
we try to add a new item to the end, though? Due to the way vectors work,
|
||||
adding a new element onto the end might require allocating new memory and
|
||||
copying the old elements over to the new space if there wasn't enough room to
|
||||
put all the elements next to each other where the vector was. If this happened,
|
||||
our reference would be pointing to deallocated memory. For more on this, see
|
||||
[The Nomicon](https://doc.rust-lang.org/stable/nomicon/vec.html).
|
||||
|
||||
Be sure to take a look at the API documentation for all the methods defined on
|
||||
`Vec` by the standard library. For example, in addition to `push` there's a
|
||||
`pop` method that will remove and return the last element. Let's move on to the
|
||||
next collection type: `String`!
|
||||
|
Loading…
Reference in New Issue
Block a user