mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-02-03 07:48:41 +08:00
remove the fun stuff
This commit is contained in:
parent
57fd1a575c
commit
a47516a171
@ -40,191 +40,8 @@ values like this is very common, and so there's a macro to do it for us:
|
|||||||
let v = vec![5, 6, 7, 8];
|
let v = vec![5, 6, 7, 8];
|
||||||
```
|
```
|
||||||
|
|
||||||
This macro does the exact same thing as our previous example, but it's much
|
This macro does a similar thing to our previous example, but it's much more
|
||||||
more convenient.
|
convenient.
|
||||||
|
|
||||||
How does this all work? Under the hood, vectors look approximately like this:
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
struct Vec<T> {
|
|
||||||
data: &mut T,
|
|
||||||
capacity: usize,
|
|
||||||
length: usize,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
the vector changes.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let mut v = Vec::new();
|
|
||||||
#
|
|
||||||
# v.push(5);
|
|
||||||
# v.push(6);
|
|
||||||
# v.push(7);
|
|
||||||
# v.push(8);
|
|
||||||
```
|
|
||||||
|
|
||||||
We've created our new vector. It will look like this:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Vec<i32> {
|
|
||||||
data: <invalid>,
|
|
||||||
capacity: 0,
|
|
||||||
length: 0,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We don't have anything stored yet, so the vector hasn't made any room for any
|
|
||||||
elements. Since we don't have any elements, `data` isn't valid either.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# let mut v = Vec::new();
|
|
||||||
#
|
|
||||||
v.push(5);
|
|
||||||
# v.push(6);
|
|
||||||
# v.push(7);
|
|
||||||
# v.push(8);
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, we insert one value into the vector. `push` looks at the current capacity
|
|
||||||
and length, and sees that there's no room for this `5` to go. Since there's
|
|
||||||
currently room for zero elements, it will request enough memory from the
|
|
||||||
operating system for a single element, and then copy `5` into that memory. It
|
|
||||||
then updates the internal details, and now they look like this:
|
|
||||||
|
|
||||||
```text
|
|
||||||
struct Vec<T> {
|
|
||||||
data: <address of first element>,
|
|
||||||
capacity: 1,
|
|
||||||
length: 1,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Our `data` now points to the start of this memory, and `capacity` and `length`
|
|
||||||
are both set to one. If they're both set to the same value, what's the
|
|
||||||
difference? We will see that shortly. But first, let's insert another value
|
|
||||||
into the vector:
|
|
||||||
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# let mut v = Vec::new();
|
|
||||||
#
|
|
||||||
# v.push(5);
|
|
||||||
v.push(6);
|
|
||||||
# v.push(7);
|
|
||||||
# v.push(8);
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
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> {
|
|
||||||
data: <address of first element>,
|
|
||||||
capacity: 2,
|
|
||||||
length: 2,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Our `capacity` and `length` both show two here. Let's try inserting another
|
|
||||||
element into the vector:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# let mut v = Vec::new();
|
|
||||||
#
|
|
||||||
# v.push(5);
|
|
||||||
# v.push(6);
|
|
||||||
v.push(7);
|
|
||||||
# v.push(8);
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
the internal state. What's it look like now?
|
|
||||||
|
|
||||||
```text
|
|
||||||
struct Vec<T> {
|
|
||||||
data: <address of first element>,
|
|
||||||
capacity: 4,
|
|
||||||
length: 3,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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 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`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# let mut v = Vec::new();
|
|
||||||
#
|
|
||||||
# v.push(5);
|
|
||||||
# v.push(6);
|
|
||||||
v.push(7);
|
|
||||||
# v.push(8);
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
the existing memory. After updating `capacity` and `len`, it looks like this:
|
|
||||||
|
|
||||||
```text
|
|
||||||
struct Vec<T> {
|
|
||||||
data: <address of first element>,
|
|
||||||
capacity: 4,
|
|
||||||
length: 4,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We can do even better than this, though. While `new` allocates a new empty
|
|
||||||
vector, we can use `with_capacity` if we know how many things we're going to
|
|
||||||
insert:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let mut v = Vec::with_capacity(4);
|
|
||||||
#
|
|
||||||
# v.push(5);
|
|
||||||
# v.push(6);
|
|
||||||
# v.push(7);
|
|
||||||
# v.push(8);
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, our initial vector looks like this:
|
|
||||||
|
|
||||||
```text
|
|
||||||
struct Vec<T> {
|
|
||||||
data: <invalid>,
|
|
||||||
capacity: 4,
|
|
||||||
length: 0,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, when we do our `push`es, we won't need to allocate until we `push` our
|
|
||||||
fifth element. The `vec!` macro uses a similar trick, so don't worry about it!
|
|
||||||
|
|
||||||
## Destroying a vector
|
## Destroying a vector
|
||||||
|
|
||||||
@ -302,7 +119,7 @@ note: immutable borrow ends here
|
|||||||
error: aborting due to previous error(s)
|
error: aborting due to previous error(s)
|
||||||
```
|
```
|
||||||
|
|
||||||
What's the matter here? Remember what can happen when we `push` to a vector: it
|
Why is this an error? Due to the way vectors work, adding a new element onto
|
||||||
might have to go get more memory and copy all of the values to that new
|
the end might require allocating new memory and copying the old elements over.
|
||||||
memory. If it has to do this, our `first` would be pointing to old, invalid
|
If this happened, our reference would be pointing to deallocated memory. For
|
||||||
memory!
|
more on this, see the Nomicon: https://doc.rust-lang.org/stable/nomicon/vec.html
|
||||||
|
Loading…
Reference in New Issue
Block a user