Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust HashMap supporting pointer stability to allow key referencing its value [duplicate]

In Rust, using a HashMap where the key is a &str that refers to a String member within its corresponding value is not directly possible. The following example illustrates the idea:

type PersonMap = HashMap<&str, Person>;

struct Person {
    name: String,
}

This is not feasible due to Rust's ownership and borrowing rules and HashMap's resizing, as explained in this answer (https://stackoverflow.com/a/61021022/84540).

However, I wonder if there exists an unordered map implementation that supports pointer stability (What is pointer stability?), which could make such usage possible. Or is it not possible due to Rust's lifetime checking? Thanks.

like image 706
nybon Avatar asked Nov 23 '25 13:11

nybon


1 Answers

You do not need pointer stability to support such a mapping, even without cloning keys. The trick is to use HashSet instead with a wrapper that makes its value behave like its key.

Here's how that could look:

use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use std::collections::HashSet;

#[derive(Debug)]
struct Person {
    name: String,
    // other fields
}

#[derive(Debug)]
struct PersonByName(Person);

impl PartialEq for PersonByName {
    fn eq(&self, other: &Self) -> bool {
        self.0.name == other.0.name
    }
}

impl Eq for PersonByName {}

impl Hash for PersonByName {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.name.hash(state);
    }
}

impl Borrow<str> for PersonByName {
    fn borrow(&self) -> &str {
        &self.0.name
    }
}

fn main() {
    let people = vec![
        Person { name: "Jimmy".to_owned() },
        Person { name: "Timmy".to_owned() },
        Person { name: "Lenny".to_owned() },
    ];
    
    let map: HashSet<PersonByName> = people.into_iter().map(PersonByName).collect();
    
    println!("{:?}", map.get("Timmy"));
}
Some(PersonByName(Person { name: "Timmy" }))

Playground link

So even though its a "set" and not a "map", we can treat it like a map by making use Rust's Borrow trait when .get-ing a value. The Hash and PartialEq/Eq are there to make it behave like just a string so the HashSet lookup is consistent.

like image 57
kmdreko Avatar answered Nov 25 '25 08:11

kmdreko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!