Lesson 4
HashSets in Rust
Introduction to HashSets in Rust

Hello! Today, we are going to explore HashSets, a powerful data structure in Rust that belongs to the collections module. HashSets provide us with an efficient way to store and manage unique items. As we delve into this lesson, you'll learn how to create, manipulate, and leverage the power of HashSets to solve common programming problems.

Rust's HashSet is an unordered collection that uses a hash function to manage its elements, ensuring that each element is unique. This makes HashSets incredibly useful for tasks where you need to check for membership, eliminate duplicates, or perform set operations. Let's get started!

Creating a HashSet

In Rust, creating a HashSet involves using the HashSet struct from the std::collections module. You can either create an empty HashSet and then add elements to it or create a Hashset with default values.

Here's how to create a HashSet:

Rust
1use std::collections::HashSet; 2 3fn main() { 4 // Create an empty HashSet 5 let mut empty_set: HashSet<i32> = HashSet::new(); 6 let mut set = HashSet::from([1,2,3,4]); 7}
  • We first import the HashSet struct from the std::collections module.
  • We then create an empty HashSet named empty_set, which can store i32 values.
  • We then create a HashSet named set, which already contains some values.
Adding and Removing Elements

Once you have a HashSet, you can add or remove elements using the insert and remove methods.

Rust
1use std::collections::HashSet; 2fn main() { 3 let mut hashset: HashSet<i32> = HashSet::new(); 4 5 // Add values to HashSet 6 hashset.insert(1); 7 hashset.insert(2); 8 hashset.insert(3); 9 10 // Remove values from HashSet 11 hashset.remove(&2); 12}
  • The insert method adds a value to the HashSet. If the value already exists, it will not be added again.
  • The remove method removes a value from the HashSet, if it exists. The value passed into remove must always be a reference.
Checking Membership and Other Properties

One of the key advantages of using a HashSet is the ability to quickly check if an item exists within the set. You can also check the length of the HashSet and whether it's empty.

Rust
1use std::collections::HashSet; 2fn main() { 3 let mut hashset: HashSet<i32> = HashSet::new(); 4 5 hashset.insert(1); 6 hashset.insert(3); 7 8 // Check membership in HashSet 9 let has_one = hashset.contains(&1); 10 let has_two = hashset.contains(&2); 11 println!("HashSet has 1: {}, has 2: {}", has_one, has_two); // Prints: "HashSet has 1: true, has 2: false" 12 13 // len() - get the number of elements 14 let length = hashset.len(); 15 println!("Length of HashSet: {}", length); // Prints: "Length of HashSet: 2" 16 17 // is_empty() - check if the set is empty 18 let is_empty = hashset.is_empty(); 19 println!("Is HashSet empty: {}", is_empty); // Prints: "Is HashSet empty: false" 20}
  • The contains method checks whether a value exists in the HashSet and returns a boolean. contains always expects a reference as an input.
  • The len method returns the number of elements in the HashSet.
  • The is_empty method checks if the HashSet is empty.
Understanding Ownership in HashSets

As with other data structures in Rust, managing ownership and borrowing is crucial when working with HashSets. Elements added to a HashSet must adhere to Rust's ownership rules.

Rust
1use std::collections::HashSet; 2fn main() { 3 let s = String::from("Hello"); 4 let mut set = HashSet::from([s, String::from("World")]); 5 println!("{}", s); // Causes an error 6}
  • We create a String called s
  • We create a HashSet set, containing s and another String element.
  • Adding s to the Hashset transfers ownership of "Hello" from s to set
HashSets as Function Parameters

HashSets can be passed to functions as references or by value. Understanding how to pass HashSets to functions allows for more modular and reusable code. Unlike tuples and arrays, a HashSet is never copy type, even if the data held in the HashSet is copy type.

Rust
1use std::collections::HashSet; 2fn main() { 3 let mut hashset = HashSet::new(); 4 hashset.insert(10); 5 hashset.insert(20); 6 7 display_hashset_reference(&hashset); 8 println!("After display_hashset_reference: {:?}", hashset); // Prints: "After display_hashset_reference: {10, 20}" 9 10 display_hashset_ownership(hashset); // Ownership moved to display_hashset_ownership 11 println!("After display_hashset_ownership: {:?}", hashset); // Causes error 12} 13 14fn display_hashset_reference(set: &HashSet<i32>) { 15 println!("In display_hashset_reference: {:?}", set); // Prints: "In display_hashset_reference: {10, 20}" 16} 17 18fn display_hashset_ownership(set: HashSet<i32>) { 19 println!("In display_hashset_ownership: {:?}", set); // Prints: "In display_hashset_ownership: {10, 20}" 20}
  • display_hashset_reference takes a reference to a HashSet, so it doesn't take ownership, allowing the HashSet to remain available after the function call.
  • display_hashset_ownership takes a HashSet by value. Even though the elements are a copyable data type (i32), a HashSet is not copy type, thus ownership is transferred.
Summary and Next Steps

Great job! You've learned how to create and manipulate HashSets in Rust, explored how to add and remove elements, checked for membership and other properties, understood ownership within HashSets, and passed HashSets to functions.

HashSets are a versatile and efficient tool in Rust for managing collections of unique items. By applying these concepts, you can make your Rust programs more effective and sophisticated.

Now it's time to put these concepts into practice. Head over to the practice exercises to reinforce your understanding and get hands-on experience with HashSets. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.