Sling Academy
Home/Rust/Rust - Converting between different collection types: from Vec<T> to HashSet<T> or HashMap<T, U>

Rust - Converting between different collection types: from Vec to HashSet or HashMap

Last updated: January 04, 2025

In Rust, collections are used to store and manipulate groups of data. The most common collection types include Vec<T>, HashSet<T>, and HashMap<K, V>. Each type has different characteristics, making them suitable for various scenarios. A Vec<T>, short for vector, is a dynamically-sized array. A HashSet<T> stores unique values of type T, while a HashMap<K, V> stores key-value pairs, with keys of type K and values of type V. In this article, we'll cover how to convert a Vec<T> into a HashSet<T> or a HashMap<T, U>, with clear examples for each conversion.

Converting Vec<T> to HashSet<T>

One of the most common reasons to convert a Vec into a HashSet is to remove duplicates from the vector. Since a HashSet keeps only unique elements, this is a straightforward conversion.

Here’s an example demonstrating how to perform this conversion:

use std::collections::HashSet;

fn main() {
    let vec = vec![1, 2, 2, 3, 4, 4, 5];
    let set: HashSet<i32> = vec.into_iter().collect();

    println!("HashSet contains: {:?}", set);
}

In this code, into_iter() converts the Vec into an iterator, and collect() constructs a HashSet from the elements of the iterator. The result is a HashSet containing only unique elements: {1, 2, 3, 4, 5}.

Converting Vec<T> to HashMap<T, U>

Converting a Vec<T> to a HashMap<T, U> requires specifying how to derive the key and value for each entry in the HashMap. This generally involves iterating over the elements of the vector and transforming them into key-value pairs.

Suppose we have a vector of tuples, and we want to create a HashMap where the first element of each tuple is the key and the second element is the value. Here’s how you can achieve this:

use std::collections::HashMap;

fn main() {
    let vec = vec![("apple", 3), ("banana", 2), ("pear", 5)];

    let map: HashMap<&str, i32> = vec.into_iter().collect();

    println!("HashMap contains: {:?}", map);
}

Here, into_iter() converts the Vec into an iterator, and collect() constructs a HashMap from the iterator. The resulting map is {"apple": 3, "banana": 2, "pear": 5}.

In some cases, you might have a vector of just values and wish to convert it into a HashMap by deriving keys algorithmically from the values, such as by using the values' indices as keys:

fn main() {
    let vec = vec!["alpha", "beta", "gamma"];

    let map: HashMap<usize, &str> = vec.iter().enumerate().map(|(i, &val)| (i, val)).collect();

    println!("Derived HashMap: {:?}", map);
}

This code uses enumerate() to pair each element with its index, then map() transforms these pairs into the desired key-value pairs for the HashMap. The final map is {0: "alpha", 1: "beta", 2: "gamma"}.

Conclusion

Understanding how to transform between different collection types like Vec<T>, HashSet<T>, and HashMap<K, V> is crucial for efficient data manipulation in Rust. Knowing these conversions allows for better management of data structures, enabling optimized operations like duplicates removal and mapping relationships between data. Leveraging into_iter(), iter(), map(), and collect() generic methods makes these transformations seamless and conveys Rust's expressive power.

Next Article: Rust - Safely handling out-of-bounds vector indexing with get and get_mut

Previous Article: Rust - Building hierarchical data structures with vectors of enumerations

Series: Collections in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior