Hash Maps

Using HashMap<K, V> to store key-value pairs.

Intermediate⏱️ 35 min📚 Prerequisites: 1

Hash Maps

HashMap<K, V> stores key-value pairs, where each key is unique.

Creating a HashMap

RUST
use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

Accessing Values

RUST
let team_name = String::from("Blue");
let score = scores.get(&team_name);  // Option<&V>

Iteration

RUST
for (key, value) in &scores {
    println!("{}: {}", key, value);
}

Updating Values

Overwriting

RUST
scores.insert(String::from("Blue"), 25);  // Overwrites 10

Only if Key Doesn't Exist

RUST
scores.entry(String::from("Yellow")).or_insert(50);

Updating Based on Existing Value

RUST
let text = "hello world wonderful world";
let mut map = HashMap::new();

for word in text.split_whitespace() {
    let count = map.entry(word).or_insert(0);
    *count += 1;
}

Ownership

RUST
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");

let mut map = HashMap::new();
map.insert(field_name, field_value);
// field_name and field_value are no longer usable!

Code Examples

Basic HashMap

Creating and using a HashMap

RUST
use std::collections::HashMap;
fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    println!("Scores: {:?}", scores);
}

Explanation:

HashMap stores key-value pairs. The insert() method adds pairs. Keys must be unique.

Accessing Values

Accessing HashMap values

RUST
use std::collections::HashMap;
fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    let team_name = String::from("Blue");
    let score = scores.get(&team_name);
    match score {
        Some(value) => println!("Blue team score: {}", value),
        None => println!("No such team"),
    }
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
}

Explanation:

The get() method returns Option<&V>. If the key exists, Some(value), if not, None. You can iterate with a for loop.

Entry API

Using Entry to update values

RUST
use std::collections::HashMap;
fn main() {
    let text = "hello world wonderful world";
    let mut map = HashMap::new();
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
    println!("{:?}", map);
}

Explanation:

The entry() API allows us to check if a key exists. or_insert() only adds a value if the key doesn't exist. Then we can modify the value.

Blockchain: Account Balances

Using HashMap for blockchain account balances

RUST
use std::collections::HashMap;
fn main() {
    let mut balances: HashMap<String, u64> = HashMap::new();
    balances.insert(String::from("0xAlice"), 1000);
    balances.insert(String::from("0xBob"), 500);
    balances.insert(String::from("0xCharlie"), 200);
    if let Some(balance) = balances.get("0xAlice") {
        println!("Alice's balance: {} tokens", balance);
    }
    if let Some(alice_balance) = balances.get_mut("0xAlice") {
        *alice_balance -= 100;
    }
    if let Some(bob_balance) = balances.get_mut("0xBob") {
        *bob_balance += 100;
    }
    println!("\nFinal balances:");
    for (address, balance) in &balances {
        println!("{}: {} tokens", address, balance);
    }
}

Explanation:

HashMaps are essential for blockchain state management. They map addresses to balances, allowing fast lookups and updates. This is how most blockchains store account state.

Blockchain: Transaction Nonces

Tracking transaction nonces with HashMap

RUST
use std::collections::HashMap;
fn main() {
    let mut nonces: HashMap<String, u64> = HashMap::new();
    nonces.insert(String::from("0xAlice"), 0);
    nonces.insert(String::from("0xBob"), 0);
    fn process_transaction(nonces: &mut HashMap<String, u64>, sender: &str) {
        let current_nonce = nonces.entry(sender.to_string()).or_insert(0);
        *current_nonce += 1;
        println!("{} sent transaction with nonce {}", sender, current_nonce);
    }
    process_transaction(&mut nonces, "0xAlice");
    process_transaction(&mut nonces, "0xAlice");
    process_transaction(&mut nonces, "0xBob");
    println!("\nFinal nonces:");
    for (address, nonce) in &nonces {
        println!("{}: {}", address, nonce);
    }
}

Explanation:

Nonces prevent replay attacks in blockchain. Each address has a nonce that increments with each transaction. HashMaps efficiently track nonces for all addresses.

Exercises

HashMap Practice

Create a HashMap, add some key-value pairs, and print all pairs!

Medium

Starter Code:

RUST
use std::collections::HashMap;
fn main() {
}

Blockchain: Balance Tracker

Create a HashMap to track account balances!

Easy

Starter Code:

RUST
use std::collections::HashMap;
fn main() {
}