<buttonid="sidebar-toggle"class="icon-button"type="button"title="Toggle Table of Contents"aria-label="Toggle Table of Contents"aria-controls="sidebar">
<ahref="print.html"title="Print this book"aria-label="Print this book">
<iid="print-button"class="fa fa-print"></i>
</a>
</div>
</div>
<divid="search-wrapper"class="hidden">
<formid="searchbar-outer"class="searchbar-outer">
<inputtype="search"name="search"id="searchbar"name="searchbar"placeholder="Search this book ..."aria-controls="searchresults-outer"aria-describedby="searchresults-header">
<p>Rust has many more types of collections. You can see them at https://doc.rust-lang.org/beta/std/collections/ in the standard library. That page has good explanations for why to use one type, so go there if you don't know what type you want. These collections are all inside <code>std::collections</code> in the standard library. The best way to use them is with a <code>use</code> statement, like we did with our <code>enums</code>. We will start with <code>HashMap</code>, which is very common.</p>
<p>A HashMap is a collection made out of <em>keys</em> and <em>values</em>. You use the key to look up the value that matches the key. You can create a new <code>HashMap</code> with just <code>HashMap::new()</code> and use <code>.insert(key, value)</code> to insert items.</p>
<p>A <code>HashMap</code> is not in order, so if you print every key in a <code>HashMap</code> together it will probably print differently. We can see this in an example:</p>
<pre><preclass="playground"><codeclass="language-rust">use std::collections::HashMap; // This is so we can just write HashMap instead of std::collections::HashMap every time
struct City {
name: String,
population: HashMap<u32, u32>, // This will have the year and the population for the year
}
fn main() {
let mut tallinn = City {
name: "Tallinn".to_string(),
population: HashMap::new(), // So far the HashMap is empty
};
tallinn.population.insert(1372, 3_250); // insert three dates
tallinn.population.insert(1851, 24_000);
tallinn.population.insert(2020, 437_619);
for (year, population) in tallinn.population { // The HashMap is HashMap<u32, u32> so it returns a two items each time
println!("In the year {} the city of {} had a population of {}.", year, tallinn.name, population);
}
}
</code></pre></pre>
<p>This prints:</p>
<pre><codeclass="language-text">In the year 1372 the city of Tallinn had a population of 3250.
In the year 2020 the city of Tallinn had a population of 437619.
In the year 1851 the city of Tallinn had a population of 24000.
</code></pre>
<p>or it might print:</p>
<pre><codeclass="language-text">In the year 1851 the city of Tallinn had a population of 24000.
In the year 2020 the city of Tallinn had a population of 437619.
In the year 1372 the city of Tallinn had a population of 3250.
</code></pre>
<p>You can see that it's not in order.</p>
<p>If you want a <code>HashMap</code> that you can sort, you can use a <code>BTreeMap</code>. Actually they are very similar to each other, so we can quickly change our <code>HashMap</code> to a <code>BTreeMap</code> to see. You can see that it is almost the same code.</p>
<pre><preclass="playground"><codeclass="language-rust">use std::collections::BTreeMap; // Just change HashMap to BTreeMap
struct City {
name: String,
population: BTreeMap<u32, u32>, // Just change HashMap to BTreeMap
}
fn main() {
let mut tallinn = City {
name: "Tallinn".to_string(),
population: BTreeMap::new(), // Just change HashMap to BTreeMap
};
tallinn.population.insert(1372, 3_250);
tallinn.population.insert(1851, 24_000);
tallinn.population.insert(2020, 437_619);
for (year, population) in tallinn.population {
println!("In the year {} the city of {} had a population of {}.", year, tallinn.name, population);
}
}
</code></pre></pre>
<p>Now it will always print:</p>
<pre><codeclass="language-text">In the year 1372 the city of Tallinn had a population of 3250.
In the year 1851 the city of Tallinn had a population of 24000.
In the year 2020 the city of Tallinn had a population of 437619.
</code></pre>
<p>Now we will go back to <code>HashMap</code>.</p>
<p>You can get a value in a <code>HashMap</code> by just putting the key in <code>[]</code> square brackets. In this next example we will bring up the value for the key <code>Bielefeld</code>, which is <code>Germany</code>. But be careful, because the program will crash if there is no key. If you write <code>println!("{:?}", city_hashmap["Bielefeldd"]);</code> for example then it will crash, because <code>Bielefeldd</code> doesn't exist.</p>
<p>If you are not sure that there will be a key, you can use <code>.get()</code> which returns an <code>Option</code>. If it exists it will be <code>Some(value)</code>, and if not you will get <code>None</code> instead of crashing the program. That's why <code>.get()</code> is the safer way to get a value from a <code>HashMap</code>.</p>
<p>This prints <code>Some("L\'Allemagne Moderne")</code> because there was already a key for <code>1</code>, so we didn't insert <code>Le Petit Prince</code>.</p>
<p><code>HashMap</code> has a very interesting method called <code>.entry()</code> that you definitely want to try out. With it you can try to make an entry and use another method like <code>.or_insert()</code> to insert the value if there is no key. The interesting part is that it also gives a mutable reference so you can change it if you want. First is an example where we just insert <code>true</code> every time we insert a book title into the <code>HashMap</code>.</p>
<p>Let's pretend that we have a library and want to keep track of our books.</p>
let book_collection = vec!["L'Allemagne Moderne", "Le Petit Prince", "Eye of the World", "Eye of the World"]; // Eye of the World appears twice
let mut book_hashmap = HashMap::new();
for book in book_collection {
book_hashmap.entry(book).or_insert(true);
}
for (book, true_or_false) in book_hashmap {
println!("Do we have {}? {}", book, true_or_false);
}
}
</code></pre></pre>
<p>This prints:</p>
<pre><codeclass="language-text">Do we have Eye of the World? true
Do we have Le Petit Prince? true
Do we have L'Allemagne Moderne? true
</code></pre>
<p>But that's not exactly what we want. Maybe it would be better to count the number of books so that we know that there are two copies of <em>Eye of the World</em>. First let's look at what <code>.entry()</code> does, and what <code>.or_insert()</code> does. <code>.entry()</code> actually returns an <code>enum</code> called <code>Entry</code>:</p>
<p><ahref="https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html">Here is the page for Entry</a>. Here is a simple version of its code. <code>K</code> means key and <code>V</code> means variable.</p>
</span>fn or_insert(self, default: V) ->&mut V { // 🚧
match self {
Occupied(entry) => entry.into_mut(),
Vacant(entry) => entry.insert(default),
}
}
<spanclass="boring">}
</span></code></pre></pre>
<p>The interesting part is that it returns a <code>mut</code> reference: <code>&mut V</code>. That means you can use <code>let</code> to attach it to a variable, and change the variable to change the value in the <code>HashMap</code>. So for every book we will insert a 0 if there is no entry. And if there is one, we will use <code>+= 1</code> on the reference to increase the number. Now it looks like this:</p>
let book_collection = vec!["L'Allemagne Moderne", "Le Petit Prince", "Eye of the World", "Eye of the World"];
let mut book_hashmap = HashMap::new();
for book in book_collection {
let return_value = book_hashmap.entry(book).or_insert(0); // return_value is a mutable reference. If nothing is there, it will be 0
*return_value +=1; // Now return_value is at least 1. And if there was another book, it will go up by 1
}
for (book, number) in book_hashmap {
println!("{}, {}", book, number);
}
}
</code></pre></pre>
<p>The important part is <code>let return_value = book_hashmap.entry(book).or_insert(0);</code>. If you take out the <code>let</code>, you get <code>book_hashmap.entry(book).or_insert(0)</code>. Without <code>let</code> it does nothing: it inserts 0, and nobody takes the mutable reference to 0. So we bind it to <code>return_value</code> so we can keep the 0. Then we increase the value by 1, which gives at least 1 for every book in the <code>HashMap</code>. Then when <code>.entry()</code> looks at <em>Eye of the World</em> again it doesn't insert anything, but it gives us a mutable 1. Then we increase it to 2, and that's why it prints this:</p>
<p>You can also do things with <code>.or_insert()</code> like insert a vec and then push into the vec. Let's pretend that we asked men and women on the street what they think of a politician. They give a rating from 0 to 10. Then we want to put the numbers together to see if the politician is more popular with men or women. It can look like this:</p>
<p>The important line is: <code>survey_hash.entry(item.0).or_insert(Vec::new()).push(item.1);</code> So if it sees "female" it will check to see if there is "female" already in the <code>HashMap</code>. If not, it will insert a <code>Vec::new()</code>, then push the number in. If it sees "female" already in the <code>HashMap</code>, it will not insert a new Vec, and will just push the number into it.</p>
<h3><aclass="header"href="#hashset-and-btreeset"id="hashset-and-btreeset">HashSet and BTreeSet</a></h3>
<p>A <code>HashSet</code> is actually a <code>HashMap</code> that only has keys. On <ahref="https://doc.rust-lang.org/std/collections/struct.HashSet.html">the page for HashSet</a> it explains this on the top:</p>
<p><code>A hash set implemented as a HashMap where the value is ().</code> So it's a <code>HashMap</code> with keys, no values.</p>
<p>You often use a <code>HashSet</code> if you just want to know if a key exists, or doesn't exist.</p>
<p>Imagine that you have 100 random numbers, and each number between 1 and 100. If you do this, some numbers will appear more than once, while some won't appear at all. If you put them into a <code>HashSet</code> then you will have a list of all the numbers that appeared.</p>
<p>A <code>BTreeSet</code> is similar to a <code>HashSet</code> in the same way that a <code>BTreeMap</code> is similar to a <code>HashMap</code>. If we print each item in the <code>HashSet</code>, we don't know what the order will be:</p>
<p>Maybe it will print this: <code>67 28 42 25 95 59 87 11 5 81 64 34 8 15 13 86 10 89 63 93 49 41 46 57 60 29 17 22 74 43 32 38 36 76 71 18 14 84 61 16 35 90 56 54 91 19 94 44 3 0 68 80 51 92 24 20 82 26 58 33 55 96 37 66 79 73</code>. But it will almost never print it in the same way again.</p>
<p>Here as well, it is easy to change your <code>HashSet</code> to a <code>BTreeSet</code> if you decide you need ordering. In our code, we only need to make two changes to switch from a <code>HashSet</code> to a <code>BTreeSet</code>.</p>
<pre><preclass="playground"><codeclass="language-rust">use std::collections::BTreeSet; // Change HashSet to BTreeSet
<p>A <code>BinaryHeap</code> is an interesting collection type, because it is mostly unordered but has a bit of order. It keeps the largest item in the front, but the other items are in any order.</p>
<p>We will use another list of items for an example, but this time smaller.</p>
fn show_remainder(input: &BinaryHeap<i32>) -> Vec<i32> { // This function shows the remainder in the BinaryHeap. Actually an iterator would be
// faster than a function - we will learn them later.
let mut remainder_vec = vec![];
for number in input {
remainder_vec.push(*number)
}
remainder_vec
}
fn main() {
let many_numbers = vec![0, 5, 10, 15, 20, 25, 30]; // These numbers are in order
let mut my_heap = BinaryHeap::new();
for number in many_numbers {
my_heap.push(number);
}
while let Some(number) = my_heap.pop() { // .pop() returns Some(number) if a number is there, None if not. It pops from the front
println!("Popped off {}. Remaining numbers are: {:?}", number, show_remainder(&my_heap));
Popped off 20. Remaining numbers are: [15, 10, 5, 0]
Popped off 15. Remaining numbers are: [10, 0, 5]
Popped off 10. Remaining numbers are: [5, 0]
Popped off 5. Remaining numbers are: [0]
Popped off 0. Remaining numbers are: []
</code></pre>
<p>You can see that the number in the 0 index is always largest: 25, 20, 15, 10, 5, then 0. But the other ones are all different.</p>
<p>A good way to use a <code>BinaryHeap</code> is for a collection of things to do. Here we create a <code>BinaryHeap<(u8, &str)></code> where the <code>u8</code> is a number for the importance of the task. The <code>&str</code> is a description of what to do.</p>
<p>A <code>VecDeque</code> is a <code>Vec</code> that is good at popping items both off the front and the back. Rust has <code>VecDeque</code> because a <code>Vec</code> is great for popping off the back (the last item), but not so great off the front. When you use <code>.pop()</code> on a <code>Vec</code>, it just takes off the last item on the right and nothing else is moved. But if you take it off another part, all the items to the right are moved over one position to the left. You can see this in the description for <code>.remove()</code>:</p>
<pre><codeclass="language-text">Removes and returns the element at position index within the vector, shifting all elements after it to the left.
<p>it will remove <code>9</code>. <code>8</code> in index 1 will move to index 0, <code>7</code> in index 2 will move to index 1, and so on. Imagine a big parking lot where every time one car leaves all the cars on the right side have to move over.</p>
<p>This, for example, is a <em>lot</em> of work for the computer. In fact, if you run it on the Playground it will probably just give up because it's too much work.</p>
<p>This is a <code>Vec</code> of 600,000 zeros. Every time you use <code>remove(0)</code> on it, it moves each zero left one space to the left. And then it does it 600,000 times.</p>
<p>You don't have to worry about that with a <code>VecDeque</code>. It is usually a bit slower than a <code>Vec</code>, but if you have to do things on both ends then it is much faster. You can just use <code>VecDeque::from</code> with a <code>Vec</code> to make one. Our code above then looks like this:</p>
my_vec.pop_front(); // pop_front is like .pop but for the front
}
}
</code></pre></pre>
<p>It is now much faster, and on the Playground it finishes in under a second instead of giving up.</p>
<p>In this next example we have a <code>Vec</code> of things to do. Then we make a <code>VecDeque</code> and use <code>.push_front()</code> to put them at the front, so the first item we added will be on the right. But each item we push is a <code>(&str, bool)</code>: <code>&str</code> is the description and <code>false</code> means it's not done yet. We use our <code>done()</code> function to pop an item off the back, but we don't want to delete it. Instead, we change <code>false</code> to <code>true</code> and push it at the front so that we can keep it.</p>