Sling Academy
Home/Rust/Porting C++ STL usage to Rust’s Vec and HashMap: key differences

Porting C++ STL usage to Rust’s Vec and HashMap: key differences

Last updated: January 04, 2025

When transitioning from C++ to Rust, developers may encounter various language-specific programming constructs and libraries that operate in a distinct manner. One such instance is the use of the Standard Template Library (STL) in C++ and its counterparts in Rust, namely Vec and HashMap. This article provides an in-depth comparison and guidance on how to port C++ STL usage to Rust's collections while highlighting key differences and similarities.

Understanding C++ STL Containers

In C++, the Standard Template Library (STL) provides generic classes and functions for data structures and algorithms. The most commonly used STL containers are std::vector and std::map. Let's explore the basic functionality of these structures:


#include <iostream>
#include <vector>
#include <map>

int main() {
    std::vector<int> numbers;
    numbers.push_back(1);
    numbers.push_back(2);
    numbers.push_back(3);

    std::map<std::string, int> wordCount;
    wordCount["hello"] = 1;
    wordCount["world"] = 2;

    return 0;
}

In this C++ code snippet, std::vector is used to store an ordered list of integers, while std::map holds pairs of words and their respective counts.

Switching to Rust Collections

In Rust, the collections that correspond to std::vector and std::map are Vec and HashMap respectively. Below is how you would represent the same logic using Rust:


fn main() {
    let mut numbers: Vec<i32> = Vec::new();
    numbers.push(1);
    numbers.push(2);
    numbers.push(3);

    use std::collections::HashMap;
    let mut word_count: HashMap<String, i32> = HashMap::new();
    word_count.insert(String::from("hello"), 1);
    word_count.insert(String::from("world"), 2);
}

The transition from C++ to Rust involves some syntactical adjustments. One of the most notable differences is the use of Vec::new() to initialize a vector, which is akin to C++'s std::vector. While both languages use generics to specify the type of elements, Rust is exceptionally efficient at handling compile-time checks for borrow and memory safety through its ownership model.

Key Differences in Use and Behavior

Memory management and safety are crucial differentiators between C++ and Rust's approach to collections:

  • Memory Safety: Unlike C++, Rust enforces strict access and modification rules through its ownership system, ensuring no undefined behavior like buffer overruns in data structures.
  • Initialization: In C++, automatic resizing is typical during initialization, while in Rust, it's common to use utilities like with_capacity to define an initial size to minimize allocations.
  • Iterators: Both languages support powerful iterators, but Rust’s iter(), iter_mut(), and into_iter() methods offer fine-grained control over mutability and ownership in loops.

Example of Iteration and Mutation

Here is an example of iterating and modifying elements within a collection:


std::vector<int> numbers = {1, 2, 3};
for (auto &num : numbers) {
    num *= 2;
}

let mut numbers = vec![1, 2, 3];
for num in &mut numbers {
    *num *= 2;
}

In the C++ example, a reference is used to iterate and modify elements directly. In the Rust example, &mut indicates mutable borrowing. Rust’s borrow checker ensures safe concurrent modifications, preventing potential data races.

Conclusion

Porting STL usage from C++ to Rust isn't just about translating code syntax; it's about embracing Rust's philosophy of safety and concurrency. Converting std::vector to Rust’s Vec and std::map to HashMap must involve rewriting sections with attention to Rust's unique features and ensuring code adheres to Rustacean best practices like using lifetimes and ownership correctly. With experience, Rust’s strengths lead to robust, performant, and error-free development as projects transition from C++ paradigms to Rust's unique model.

Next Article: Rust - Simulating a queue with a Vec by removing from front vs using VecDeque

Previous Article: Rust - Creating a dynamic configuration store with a HashMap of string keys and values

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