Sling Academy
Home/Rust/Building a library of custom data structures based on Rust vectors and maps

Building a library of custom data structures based on Rust vectors and maps

Last updated: January 04, 2025

When working with Rust, it's often beneficial to create custom data structures that extend the capabilities of standard containers such as vectors and maps. Rust’s powerful type system, along with its safety features, make it an ideal language for such tasks. In this article, we will explore how to build a custom library of data structures based on Rust’s vectors and hash maps, demonstrating concepts with examples.

Understanding Rust Vectors

Rust vectors, defined as Vec<T>, are a re-sizable array type. They are often used when dealing with a list of items of the same type. Creating a custom data structure with vectors involves understanding how to work with their inherent features like growth values and memory management. Here’s a simple example:

fn main() {
    let mut numbers: Vec<i32> = Vec::new();
    numbers.push(1);
    numbers.push(2);
    numbers.push(3);
    println!("Vector contains: {:?}", numbers);
}

In this example, we initialize an empty vector of integers and use the push method to add elements. This flexibility of appending items makes vectors particularly useful for dynamic collections.

Creating Custom Structures with Vectors

We can harness the capabilities of vectors in a custom data structure by using the struct keyword. This enables us to bundle other related attributes or methods alongside the vector data:

struct StudentScores {
    scores: Vec<i32>,
}

impl StudentScores {
    fn new() -> StudentScores {
        StudentScores { scores: Vec::new() }
    }

    fn add_score(&mut self, score: i32) {
        self.scores.push(score);
    }

    fn average_score(&self) -> f32 {
        let sum: i32 = self.scores.iter().sum();
        sum as f32 / self.scores.len() as f32
    }
}

The StudentScores structure uses a vector to store scores for students, offering methods to add scores and calculate their average. This custom structure organizes related behavior together with the data.

Leveraging Hash Maps in Rust

Hash maps, or HashMap<K, V>, provide a mechanism for storing key-value pairs, which are optimal for lookups. They are comparable to dictionaries in other programming languages. A custom data structure can harness this for effective data retrieval.

use std::collections::HashMap;

fn main() {
    let mut user_info = HashMap::new();
    user_info.insert("username", "rustacean");
    user_info.insert("email", "[email protected]");
    println!("User map: {:?}", user_info);
}

In this snippet, a hash map is used to associate a username and email. We can now look at building more complex structures using hash maps.

Crafting Custom Structures with Hash Maps

To create a library of richer data structures, consider combining hash maps with other constructs, like so:

use std::collections::HashMap;

struct Directory {
    entries: HashMap<String, String>,
}

impl Directory {
    fn new() -> Directory {
        Directory { entries: HashMap::new() }
    }

    fn add_entry(&mut self, name: String, phone: String) {
        self.entries.insert(name, phone);
    }

    fn get_phone(&self, name: &String) -> Option<&String> {
        self.entries.get(name)
    }
}

The Directory structure makes effective use of a hash map to store a phonebook. The ability to add and retrieve entries allows for extensible use-cases in applications.

Organizing Code in Rust Libraries

To make our custom data structures reusable, it’s crucial to understand how to organize them in libraries. Rust promotes the use of module systems to compartmentalize code.

// lib.rs
pub mod student_scores;

// student_scores.rs
pub struct StudentScores {
    ... // Similar as above
}

By structuring your code with modules, you create libraries that can be easily shared and extended. This encapsulates functionality and can expose only desired parts of the API, maintaining abstraction.

Conclusion

Combining Rust's vectors and hash maps into custom data structures harnesses the language's strengths, such as memory safety and concurrency support. By creating and organizing these structures within libraries, developers can enhance productivity, ensure reliability, and facilitate code reuse. Whether creating a nested vector-based hierarchy or a hash map-backed database, understanding these building blocks leads to robust applications. Practice building these structures, and soon, the potential for innovative, efficient Rust programs becomes vast.

Next Article: Rust - Storing state machines in HashMaps keyed by states or transitions

Previous Article: Rust - Handling nested or hierarchical HashMaps for complex data relationships

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