What a set is
A Vec keeps every value you put in it, repeats included. Sometimes you only care about which values there are, not how often or in what order. A set is made for that: it holds each value once, and putting in a value it already has changes nothing.
Rust's set is HashSet. It lives in the standard library, so a file of your own needs use std::collections::HashSet; at the top. Without that line the compiler doesn't know the name.
The basic operations
use std::collections::HashSet;
fn main() {
let mut pets: HashSet<&str> = HashSet::new();
pets.insert("cat");
pets.insert("cat");
println!("{}", pets.len());
println!("{}", pets.contains("cat"));
}
This prints 1 and true. The second insert found "cat" already there.
HashSet::new()makes an empty set. Declare it withlet mut, because adding changes it.s.insert(x)putsxin the set. It returnstrueif the value was new andfalsefor a repeat.s.contains(&x)istruewhenxis in the set. It takes a reference, so for a number you writenums.contains(&2). A string literal such as"cat"already is a reference.s.len()is the number of values in the set.
The return value of insert is handy: after kids.insert("Zoe") on a set that already holds Zoe, the call gives false, and for a new name such as "Kai" it gives true.
Building a set from a vector
v.into_iter().collect() turns a vector into a set, and every repeat goes in once. The compiler can't guess the kind of collection you want, so you must write the type:
let nums: HashSet<i32> = vec![1, 2, 2, 3].into_iter().collect();
This set holds 1, 2 and 3. Without the type, the program does not compile: the compiler reports type annotations needed.
Gotchas
- Forgetting
mut. Callinginserton a set declared with plainletfails withcannot borrow `kids` as mutable, as it is not declared as mutable. - No order, no indexes. A set has no first element and no
s[0]. Printing it or looping over it can show the values in any order. To see them in a steady order, collect them into a vector and sort it. - Speed.
containson a set is fast even for a big set. On a vector it looks at the values one by one.
The lesson's task
A camp's sign-up vector names has some names twice. The task: build a set kids from it, add Zoe and then Ava, and print Leo is coming if Leo is in the set.
use std::collections::HashSet;
fn main_function() {
let names = vec!["Ava", "Leo", "Ava", "Mia", "Leo"];
let mut kids: HashSet<&str> = names.into_iter().collect();
println!("{} kids", kids.len());
kids.insert("Zoe");
kids.insert("Ava");
println!("{} kids", kids.len());
if kids.contains("Leo") {
println!("Leo is coming");
}
}
The output is:
3 kids
4 kids
Leo is coming
The vector has five names but only three different ones (Ava, Leo, Mia), so the first count is 3. Zoe is new and raises the count to 4. Ava is already in the set, so inserting her again changes nothing.
Key takeaways
- A
HashSetholds each value once; inserting a repeat changes nothing. insert,containsandlenare the three basics.- Build one from a vector with
into_iter().collect()and write the set's type. - Declare it with
let mutif you will add to it. - A set has no order and no indexes.
