-
Notifications
You must be signed in to change notification settings - Fork 3.9k
some work on collections #137
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
Changes from 26 commits
4de5854
d9591c1
57fd1a5
a47516a
c9a21c8
65691ee
c9044e9
b1525a9
91396aa
2570566
cd5517b
f80bcca
a417244
8f1b922
854614f
1da1e81
99fc51d
9ba5bb3
00e5b22
ccd4dcf
be6a7d5
50acc06
dcccf74
b168c61
6bef3b8
0812f3a
c3ddfa5
d56e536
021998f
2d5ea64
84b135d
a7e3b2a
7280821
d7a11ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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>`]() | ||
| - [Essential Collections](ch08-00-essential-collections.md) | ||
| - [Vectors](ch08-01-vectors.md) | ||
| - [Strings](ch08-02-strings.md) | ||
| - [HashMaps](ch08-03-hashmaps.md) | ||
|
||
|
|
||
| - [Error Handling]() | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Essential 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 `String` before, but | ||
| we'll talk about it in depth now. | ||
| * A *HashMap* 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. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| ## 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). | ||
|
|
||
| Be sure to take a look at the API documentation for all the 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`! |
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.
ugh i know we already talked about this, but I would really prefer something other than "essential." How about "fundamental"?
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.
YOU DID THIS 57fd1a5
(but yeah fundamental is fine)