This post shows how to check if a key exists in a HashMap in Rust. As we know, Rust has the HashMap struct, which is part of its Collections library that we can use to store key-value pairs.
Rust HashMap – Strings as Keys
The HashMap struct uses generics. With generics, we can define the data type of the keys and their values. Consider the following codes.
1 2 3 4 | let mut hash_map:HashMap<String, String> = HashMap::new(); hash_map.insert("1".to_string(), "A".to_string()); hash_map.insert("2".to_string(), "B".to_string()); |
We have an instance of HashMap whose keys and values are of type String. To check if one of our keys is in the HashMap instance, consider the following codes. Super easy, right?
1 2 3 4 5 6 7 | let mut hash_map:HashMap<String, String> = HashMap::new(); hash_map.insert("1".to_string(), "A".to_string()); hash_map.insert("2".to_string(), "B".to_string()); println!("{}", hash_map.contains_key("1")); println!("{}", hash_map.contains_key("2")); println!("{}", hash_map.contains_key("3")); |
The codes generate the following output.
1 2 3 | true true false |
Numeric Keys Example
From String keys, let us explore numeric values as keys and spice things up a little more. Consider these initial codes. Relax, they are very similar to the previous one. The only difference now is we are using i32 as numeric keys!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | use std::collections::HashMap; fn main() { let mut hash_map:HashMap<i32, String> = HashMap::new(); hash_map.insert(1, "Uno".to_string()); hash_map.insert(2, "Dos".to_string()); hash_map.insert(3, "Tres".to_string()); println!("{:?}", hash_map); println!("{}", hash_map.contains_key(&4)); println!("{}", hash_map.contains_key(&1)); println!("{}", hash_map.contains_key(&2)); } |
At line 4, we specify the first formal type parameter of the generics to be i32. Next, from lines 6 to 8, we insert key-value pairs to the HashMap instance – the keys are numeric values, and the values are of String type.
Then, we check if some numeric keys exist in our HashMap instance – see lines 12, 13, and 14. Notice we use the ampersand symbol before the value (or variables if we stored the values in them) to pass the reference instead of the actual value. For numeric variables, we would code something like the following.
1 2 3 4 | let tres = 3; // Prints out 3 println!("{}", hash_map.contains_key(&tres)); |
Numeric keys are still easy! We did not even break a sweat! Now, moving on – we will use our struct instance as keys. Are you excited?
Check Keys As Struct Instances
This section of the post required a little preparation. We need more codes here than in the previous parts. So, hang on tight! Since we are going to use instances of our struct, we need to prep it up.
First, we start with a simple struct design. So, we have a Person struct with person_id and person_name fields. Simple enough!
1 2 3 4 5 6 7 8 9 | use std::collections::HashMap; use std::cmp::Ordering; use std::hash::{Hash}; #[derive(Debug, Eq, Hash)] struct Person { person_id: i32, person_name: String } |
We need to use the inline implementation of Debug, Eq, and Hash traits using the derive directive. The directive implements the traits for our struct but not quite for the Eq trait.
Although we specify the Eq trait in the directive, we still need explicitly to implement the PartialEq trait. Why? There is a long discussion among Rust enthusiasts about that, but I would skip it for this post.
1 2 3 4 5 6 | impl PartialEq for Person { fn eq(&self, other: &Person) -> bool { self.person_id == other.person_id && self.person_name == other.person_name } } |
The boolean expression is important because it will match a single key (which are Person instances) in the HashMap instance.
Now, we are almost complete. Let us see the codes in the main function. First, we create a HashMap instance whose keys are instances of Person struct, and values are of String type.
1 | let mut hash_map:HashMap<Person, String> = HashMap::new(); |
Next, we fill it with Person instances.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | hash_map.insert(Person { person_id: 1, person_name: String::from("Karl") }, "1-Karl".to_string()); hash_map.insert(Person { person_id: 2, person_name: String::from("Enrico") }, "2-Enrico".to_string()); hash_map.insert(Person { person_id: 3, person_name: String::from("Chino") }, "3-Chino".to_string()); |
To check if these instances are indeed in our HashMap, we print out its content.
1 | println!("{:?}", hash_map); |
Then, we create new instances of Person struct to serve as test keys and retrieve their corresponding String values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | let keyCheck1 = Person { person_id: 1, person_name: String::from("Karl") }; let keyCheck2 = Person { person_id: 2, person_name: String::from("Enrico") }; let keyCheck3 = Person { person_id: 3, person_name: String::from("Chino") }; let keyCheck3Unknown = Person { person_id: 4, person_name: String::from("Whoo You") }; |
Notice that we created a Person instance not found in our MashMap instance. We will use it for negative testing.
Now the testing part, we have these Rust codes to check if the test keys exist in our HashMap.
1 2 3 4 | println!("{}", hash_map.contains_key(&keyCheck1)); println!("{}", hash_map.contains_key(&keyCheck2)); println!("{}", hash_map.contains_key(&keyCheck3)); println!("{}", hash_map.contains_key(&keyCheck3Unknown)); |
When we run all the codes, we will get the following output.
1 2 3 4 5 6 7 8 9 10 11 | warning: 5 warnings emitted Finished dev [unoptimized + debuginfo] target(s) in 1.80s Running `target\debug\rust-key-hashmap.exe` {Person { person_id: 3, person_name: "Chino" }: "3-Chino", Person { person_id: 2, person_name: "Enrico" }: "2-Enrico", Person { person_id: 1, person_name: "Karl" }: "1-Karl"} true true true false Process finished with exit code 0 |
These are just basic, and we can still come up with other types of keys!
This post is now part of the Rust Programming Language For Beginners Tutorial.