remove the fun stuff

This commit is contained in:
Steve Klabnik 2016-08-26 15:48:56 -04:00 committed by Carol (Nichols || Goulding)
parent 57fd1a575c
commit a47516a171

View File

@ -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