-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
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 d9591c1
Small wording, punctuation, spelling edits
carols10cents 57fd1a5
basic -> essential
steveklabnik a47516a
remove the fun stuff
steveklabnik c9a21c8
lol
steveklabnik 65691ee
start off the start of strings
steveklabnik c9044e9
strings
steveklabnik b1525a9
hashmaps
steveklabnik 91396aa
add some more
steveklabnik 2570566
Small wording edits
carols10cents cd5517b
Remove double error handling from summary
carols10cents f80bcca
Renaming files to match currently planned chapter order
carols10cents a417244
Edits to Vec - be more specific, update error messages
carols10cents 8f1b922
Make headings consistent with each other
carols10cents 854614f
Wording edits for strings
carols10cents 1da1e81
I'd rather explain _ than 'static at this point tbh
carols10cents 99fc51d
Edits and additions to HashMap
carols10cents 9ba5bb3
Make the filename and header more consistent
carols10cents 00e5b22
Remove irrelevant space special casing
carols10cents ccd4dcf
More edits and some TODOs for strings
carols10cents be6a7d5
Add a chapter summary
carols10cents 50acc06
Round off Strings section
carols10cents dcccf74
Bump in headings a level
carols10cents b168c61
Talk about how dropping of a Vec affects references into the vec
carols10cents 6bef3b8
Small wording tweaks, mostly removing 'you'
carols10cents 0812f3a
Fill in some chapter numbers we know now
carols10cents c3ddfa5
No more essence. anywhere. There is no essence.
carols10cents d56e536
You've got your big G's, I've got my hash map
carols10cents 021998f
The word of the day is *string slice*
carols10cents 2d5ea64
Crates for the crates.io!
carols10cents 84b135d
Add text between the headings
carols10cents a7e3b2a
Wrap at 80 oops
carols10cents 7280821
Show how to store different types in an enum in a vec
carols10cents d7a11ad
Paragraph foreshadowing trait objects
carols10cents File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this meant to be Chapter 25.2? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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`! |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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
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)