Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

some work on collections #137

Merged
merged 34 commits into from
Sep 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4de5854
some work on collections
steveklabnik Jul 19, 2016
d9591c1
Small wording, punctuation, spelling edits
carols10cents Jul 21, 2016
57fd1a5
basic -> essential
steveklabnik Aug 26, 2016
a47516a
remove the fun stuff
steveklabnik Aug 26, 2016
c9a21c8
lol
steveklabnik Aug 26, 2016
65691ee
start off the start of strings
steveklabnik Aug 26, 2016
c9044e9
strings
steveklabnik Aug 30, 2016
b1525a9
hashmaps
steveklabnik Aug 30, 2016
91396aa
add some more
steveklabnik Aug 31, 2016
2570566
Small wording edits
carols10cents Sep 15, 2016
cd5517b
Remove double error handling from summary
carols10cents Sep 15, 2016
f80bcca
Renaming files to match currently planned chapter order
carols10cents Sep 15, 2016
a417244
Edits to Vec - be more specific, update error messages
carols10cents Sep 15, 2016
8f1b922
Make headings consistent with each other
carols10cents Sep 16, 2016
854614f
Wording edits for strings
carols10cents Sep 16, 2016
1da1e81
I'd rather explain _ than 'static at this point tbh
carols10cents Sep 19, 2016
99fc51d
Edits and additions to HashMap
carols10cents Sep 19, 2016
9ba5bb3
Make the filename and header more consistent
carols10cents Sep 19, 2016
00e5b22
Remove irrelevant space special casing
carols10cents Sep 19, 2016
ccd4dcf
More edits and some TODOs for strings
carols10cents Sep 20, 2016
be6a7d5
Add a chapter summary
carols10cents Sep 26, 2016
50acc06
Round off Strings section
carols10cents Sep 26, 2016
dcccf74
Bump in headings a level
carols10cents Sep 26, 2016
b168c61
Talk about how dropping of a Vec affects references into the vec
carols10cents Sep 26, 2016
6bef3b8
Small wording tweaks, mostly removing 'you'
carols10cents Sep 26, 2016
0812f3a
Fill in some chapter numbers we know now
carols10cents Sep 26, 2016
c3ddfa5
No more essence. anywhere. There is no essence.
carols10cents Sep 27, 2016
d56e536
You've got your big G's, I've got my hash map
carols10cents Sep 27, 2016
021998f
The word of the day is *string slice*
carols10cents Sep 27, 2016
2d5ea64
Crates for the crates.io!
carols10cents Sep 27, 2016
84b135d
Add text between the headings
carols10cents Sep 27, 2016
a7e3b2a
Wrap at 80 oops
carols10cents Sep 27, 2016
7280821
Show how to store different types in an enum in a vec
carols10cents Sep 27, 2016
d7a11ad
Paragraph foreshadowing trait objects
carols10cents Sep 27, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
- [Controlling visibility with `pub`](ch07-02-controlling-visibility-with-pub.md)
- [Importing names with `use`](ch07-03-importing-names-with-use.md)

- [Basic Collections]()
- [Vectors]()
- [Strings]()
- [`HashMap<K, V>`]()
- [Fundamental Collections](ch08-00-fundamental-collections.md)
- [Vectors](ch08-01-vectors.md)
- [Strings](ch08-02-strings.md)
- [Hash Maps](ch08-03-hash-maps.md)

- [Error Handling]()

Expand Down
18 changes: 18 additions & 0 deletions src/ch08-00-fundamental-collections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Fundamental Collections

Rust's standard library includes a number of really useful data structures
called *collections*. Most other types represent one specific value, but
collections can contain multiple values inside of them. Each collection has
different 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:

* A *vector* allows us to store a variable number of values next to each other.
* A *string* is a collection of characters. We've seen the `String` type
before, but we'll talk about it in depth now.
* A *hash map* allows us to associate a value with a particular key.

There are more specialized variants of each of these data structures for
particular situations, but these are the most fundamental and common. We're
going to discuss how to create and update each of the collections, as well as
what makes each special.
215 changes: 215 additions & 0 deletions src/ch08-01-vectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
## Vectors

The first type we'll look at is `Vec<T>`, also known as a *vector*. Vectors
allow us 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

To create a new vector, we can call the `new` function:

```rust
let v: Vec<i32> = Vec::new();
```

Note that we added a type annotation here. Since 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 stored inside them (we'll talk about Generics more throroughly in
Chapter 10), and the angle brackets here tell Rust that this vector will hold
elements of the `i32` type.

That said, in real code, we 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
look at how to modify a vector next.

### Updating a Vector

To put elements in the vector, we can use the `push` method:

```rust
let mut v = Vec::new();

v.push(5);
v.push(6);
v.push(7);
v.push(8);
```

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];
```

This macro does a similar thing to our previous example, but it's much more
convenient.

### Dropping a Vector Drops its Elements

Like any other `struct`, a vector will be freed when it goes out of scope:

```rust
{
let v = vec![1, 2, 3, 4];

// do stuff with v

} // <- v goes out of scope and is freed here
```

When the vector gets dropped, it will also drop all of its contents, so those
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 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 third: &i32 = &v[2];
let third: Option<&i32> = v.get(2);
```

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];

let does_not_exist = &v[100];
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. Deciding which way to access
elements in a vector depends on whether we consider an attempted access past
the end of the vector to be an error, in which case we'd want the `panic!`
behavior, or whether this will happen occasionally under normal circumstances
and our code will have logic to handle getting `Some(&element)` or `None`.

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. This means in a function
that owns a `Vec`, we can't return a reference to an element since the `Vec`
will be cleaned up at the end of the function:

```rust,ignore
fn element() -> String {
let list = vec![String::from("hi"), String::from("bye")];
list[1]
}
```

Trying to compile this will result in the following error:

```bash
error: cannot move out of indexed content [--explain E0507]
|>
4 |> list[1]
|> ^^^^^^^ cannot move out of indexed content
```

Since `list` goes out of scope and gets cleaned up at the end of the function,
the reference `list[1]` cannot be returned because it would outlive `list`.

Here's another example of 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];

let first = &v[0];

v.push(6);
```

Compiling this will give us this error:

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

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).

### Using an Enum to Store Multiple Types

Let's put vectors together with what we learned about enums in Chapter 6. At
the beginning of this section, we said that vectors will only store values that
are all the same type. This can be inconvenient; there are definitely use cases
for needing to store a list of things that might be different types. Luckily,
the variants of an enum are all the same type as each other, so when we're in
this scenario, we can define and use an enum!

For example, let's say we're going to be getting values for a row in a
spreadsheet. Some of the columns contain integers, some floating point numbers,
and some strings. We can define an enum whose variants will hold the different
value types. All of the enum variants will then be the same type, that of the
enum. Then we can create a vector that, ultimately, holds different types:

```rust
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}

let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
```

This has the advantage of being explicit about what types are allowed in this
vector. If we allowed any type to be in a vector, there would be a chance that
the vector would hold a type that would cause errors with the operations we
performed on the vector. Using an enum plus a `match` where we access elements
in a vector like this means that Rust will ensure at compile time that we
always handle every possible case.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

last thing, and then let's :shipit:

We should mention "if we always know what the set of types are, an enum works. if we don't, a trait object works, and we'll learn about those later" (but in better words)


Using an enum for storing different types in a vector does imply that we need
to know the set of types we'll want to store at compile time. If that's not the
case, instead of an enum, we can use a trait object. We'll learn about those in
Chapter XX.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this meant to be Chapter 25.2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been rearranging the later chapters a lot, so we're not sure which chapter it's going to be yet.


Now that we've gone over some of the most common ways to use vectors, be sure
to take a look at the API documentation for other useful 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