ProgrammingBackend Developer

What are the features of working with HashSet and HashMap collections in Rust? How to manage ownership of keys and values, and what are the dangers of incorrect usage?

Pass interviews with Hintsage AI assistant

Answer.

Background

The HashSet and HashMap collections are standard structures from std::collections that implement fast hash-based lookup. They have been built into Rust since the early versions of the language, but the internal details of their usage often cause difficulties even for experienced developers due to the ownership system.

Issue

Confusion arises when inserting and retrieving elements (especially if values are not Copy), when altering the collection (mutable borrows), and also when using references as keys. There is also the problem of implementing proper Eq/Hash for user-defined types.

Solution

  • When adding an element, the collection takes (moves) the key/value if not using a reference or a copyable type.
  • It is safe to modify the contents only through a mutable reference to HashMap/HashSet.

Code example:

use std::collections::HashMap; fn main() { let mut map = HashMap::new(); map.insert("key", 42); if let Some(value) = map.get("key") { println!("Found value: {}", value); } }

Key features:

  • Keys in HashMap/HashSet must implement Hash, Eq
  • Inserting an element always moves (moves) the variable into the collection
  • Values can only be safely retrieved while the collection is not modified (borrow rules)

Trick questions.

Can you obtain multiple mutable references to the same HashMap element?

No, the borrow checker will not allow this to avoid ownership violations.

Can you use the string literal "abc" directly as a key in HashMap<String, V>?

No, it expects exactly String, while "abc" is &'static str. A conversion is required: insert("abc".to_string(), val).

Can you extract a value from HashMap, keeping it in a separate variable, and continue to use HashMap?

Yes, you can take a reference to the value via get — but if you do remove (or extract by move), then HashMap mutates, and any old references become invalid.

Common mistakes and anti-patterns

  • Using a reference to a temporary key during lookup (living as long as the map)
  • Implementing Hash/Eq for complex structures without considering all fields (risk of collisions or inconsistent comparisons)
  • Modifying the structure of HashMap while iterating over references to its values

Real-life example

Negative case

Attempting to borrow both the key and value at the same time, and then mutating the collection:

let mut map = HashMap::new(); map.insert("abc".to_string(), 10); let val = map.get("abc"); map.insert("def".to_string(), 20); // borrow checker error

Pros:

  • Obvious code from the novice's perspective

Cons:

  • Compilation error — cannot borrow mutably and immutably at the same time

Positive case

Extracting a value, working only with its copy or clone:

let mut map = HashMap::new(); map.insert("abc".to_string(), 10); if let Some(val) = map.get("abc") { let val = *val; // copy map.insert("def".to_string(), 20); // all works }

Pros:

  • No ownership violations, predictable code
  • Type safety

Cons:

  • Extra copying if the value is heavy