mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-01-23 23:50:25 +08:00
Small wording, punctuation, spelling edits
This commit is contained in:
parent
4de58546ae
commit
d9591c1928
@ -3,7 +3,7 @@
|
||||
Rust's standard library includes a number of really useful data structures
|
||||
called 'collections'. Most types represent one specific value, but collections
|
||||
can contain multiple values inside of them. Each collection has different
|
||||
capabilities and costs, and chosing an appropriate one for the situation you're
|
||||
capabilities and costs, and choosing an appropriate one for the situation you're
|
||||
in is a skill you'll develop over time. In this chapter, we'll go over three
|
||||
collections which are used very often in Rust programs:
|
||||
|
||||
@ -13,4 +13,4 @@ collections which are used very often in Rust programs:
|
||||
* *HashMap*s allow you to associate a value with a particular key.
|
||||
|
||||
There are more specialized variants of each of these data structures for
|
||||
special situations, but these ones are the most fundamental.
|
||||
special situations, but these are the most fundamental.
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 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
|
||||
allow you to store more than one value in a single data structure next to each
|
||||
other in memory.
|
||||
|
||||
## Creating a new vector
|
||||
@ -13,10 +13,10 @@ let v: Vec<i32> = Vec::new();
|
||||
```
|
||||
|
||||
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 sort of elements we intend to
|
||||
store. This is an important point. Vectors are homogenous; they may store many
|
||||
values, but those values must be all of the same type. Vectors are generic over
|
||||
the type you store inside them, so we use the angle brackets to tell Rust that
|
||||
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.
|
||||
|
||||
That said, in real code, you very rarely need to do this type annotation. Let's
|
||||
@ -53,14 +53,14 @@ struct Vec<T> {
|
||||
}
|
||||
```
|
||||
|
||||
This is not literally true, but will help you gain some intutions about it at
|
||||
a high level. The actual representation is quite involved, and you can [read a
|
||||
chapter in the Nomicon][nomicon] for the full details.
|
||||
This is not literally true, but will help you gain some intutions about it. The
|
||||
actual representation is quite involved, and you can [read a chapter in the
|
||||
Nomicon][nomicon] for the full details.
|
||||
|
||||
[nomicon]: https://doc.rust-lang.org/stable/nomicon/vec.html
|
||||
|
||||
At a high level, though, this is okay: a vector has a reference to some data,
|
||||
a capacity, and a length. Let's go through these lines of code, and see how
|
||||
a capacity, and a length. Let's go through these lines of code and see how
|
||||
the vector changes.
|
||||
|
||||
```rust
|
||||
@ -123,14 +123,13 @@ v.push(6);
|
||||
# v.push(8);
|
||||
```
|
||||
|
||||
Same thing, we `push` a `6`. And, in this case, the same thing will happen:
|
||||
Same thing, we `push` a `6`. In this case, the same thing will happen:
|
||||
`push` looks at the values of `capacity` and `len`, and sees that we don't have
|
||||
room for a second element. So here's what it does: it requests room for twice
|
||||
as many elements as we currently have. In this case, that means two elements.
|
||||
It then copies the existing element over into the new memory allocation, and
|
||||
then copies the `6` into the next open slot. After updating the internal
|
||||
values, it now looks like this:
|
||||
|
||||
Once it gets the memory it requested, the code in `push` copies the existing
|
||||
element over into the new memory allocation then copies the `6` into the next
|
||||
open slot. After updating the internal values, the vector now looks like this:
|
||||
|
||||
```text
|
||||
struct Vec<T> {
|
||||
@ -141,7 +140,7 @@ struct Vec<T> {
|
||||
```
|
||||
|
||||
Our `capacity` and `length` both show two here. Let's try inserting another
|
||||
elment into the vector:
|
||||
element into the vector:
|
||||
|
||||
```rust
|
||||
# let mut v = Vec::new();
|
||||
@ -154,7 +153,7 @@ v.push(7);
|
||||
|
||||
Same story here: `push` looks and sees that our vector is full. However,
|
||||
this time, something is slightly different. We currently have a capacity of
|
||||
two elements. So the vector will request memory for double that number of
|
||||
two elements, so the vector will request memory for double that number of
|
||||
elements from the operating system: four. But why does it double every time?
|
||||
We'll learn about that soon. For now, `push` will then copy the first two
|
||||
elements over to the new memory, copy our `7` onto the end, and then update
|
||||
@ -170,11 +169,12 @@ struct Vec<T> {
|
||||
|
||||
Ah ha! A difference. In essence, `capacity` tracks how much memory we've got
|
||||
saved away for holding elements. `length` keeps track of how many elements we
|
||||
are actually storing. Why not just allocate exactly how much we need? In order
|
||||
to get more heap memory, we have to talk to the operating system, and that's
|
||||
slower. Not only that, but we have to copy the contents of the vector from the
|
||||
old memory to the new memory each time. So we make a tradeoff: we allocate a
|
||||
bit more memory than we need, but in exchange, we get speed.
|
||||
are actually storing. Why not just allocate exactly as much as we need? In order
|
||||
to get more heap memory, we have to talk to the operating system, and that
|
||||
takes time. Not only that, but each time we get chunk of memory, we have to
|
||||
copy the contents of the vector from the old memory to the new memory. So we
|
||||
make a tradeoff: we request a bit more memory than we need, but in exchange,
|
||||
we speed up the next few `push` operations.
|
||||
|
||||
We can see this speed the next time we call `push`:
|
||||
|
||||
@ -189,7 +189,7 @@ v.push(7);
|
||||
|
||||
Now, `push` looks at the capacity versus the length: we have room for four
|
||||
elements, but we only have three elements. Perfect! We skip that "request more
|
||||
memory from the OS and copy everything" business, and just copy the `7` into
|
||||
memory from the OS and copy everything" business and just copy the `7` into
|
||||
the existing memory. After updating `capacity` and `len`, it looks like this:
|
||||
|
||||
```text
|
||||
@ -248,7 +248,8 @@ to introduce references to the elements of the vector. Let's tackle that next!
|
||||
|
||||
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 added in the return types for extra clarity:
|
||||
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];
|
||||
@ -258,10 +259,10 @@ let three: 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: `&`
|
||||
and `[]`s, and 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:
|
||||
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:
|
||||
|
||||
```rust,should_panic
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
@ -271,11 +272,11 @@ let does_not_exist = v.get(100);
|
||||
```
|
||||
|
||||
With the `[]`s, Rust will cause a `panic!`. With the `get` method, it will
|
||||
instead return `None`. But it's important to note that while `panic!`s 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.
|
||||
|
||||
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
|
||||
vector and will make sure that everything stays valid. For example, here's
|
||||
a case that looks okay, but actually isn't:
|
||||
|
||||
```rust,ignore
|
||||
@ -302,6 +303,6 @@ error: aborting due to previous error(s)
|
||||
```
|
||||
|
||||
What's the matter here? Remember what can happen when we `push` to a vector: it
|
||||
might have to go get more memory, and copy all of the values to that new
|
||||
might have to go get more memory and copy all of the values to that new
|
||||
memory. If it has to do this, our `first` would be pointing to old, invalid
|
||||
memory!
|
||||
|
Loading…
Reference in New Issue
Block a user