2

I'm relatively new to Rust. I want to write a function that will create a HashMap from a collection given a closure that generates a key from a value, e.g.

[derive(Debug)]
struct Foo {
    id: u32,
    name: String,
}
let foos = vec![
    Foo { id: 1, name: "Bill".to_string() },
    Foo { id: 2, name: "Mary".to_string() },
];
println!("{:?}", foos);
println!("{:?}", map_by(foos.iter(), |f|  f.id));  // borrow  the Foos
println!("{:?}", map_by(foos.iter(), |f| &f.name));          // borrow
println!("{:?}", map_by(foos.iter(), |f|  f.name.as_str())); // borrow
println!("{:?}", map_by(foos,        |f|  f.id));  // consume the Foos

I wrote this, and it works for the above uses:

fn map_by<I,K,V>(iterable: I, f: impl Fn(&V) -> K) -> HashMap<K,V>
where I: IntoIterator<Item = V>,
      K: Eq + Hash
{
    iterable.into_iter().map(|v| (f(&v), v)).collect()
}

[Foo { id: 1, name: "Bill" }, Foo { id: 2, name: "Mary" }]
{1: Foo { id: 1, name: "Bill" }, 2: Foo { id: 2, name: "Mary" }}
{"Bill": Foo { id: 1, name: "Bill" }, "Mary": Foo { id: 2, name: "Mary" }}
{"Bill": Foo { id: 1, name: "Bill" }, "Mary": Foo { id: 2, name: "Mary" }}
{2: Foo { id: 2, name: "Mary" }, 1: Foo { id: 1, name: "Bill" }}

Great! But it doesn't work when I let the HashMap OWN the values when the keys refer to them:

println!("{:?}", map_by(foos,        |f| f.name.as_str()));

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements

And unfortunately the error message melted my brain. I tried specifying that the key and value have the same lifetime, but it didn't help. Here it is in a playground. What is the compiler trying to tell me, and what am I doing wrong? Surely if this can work when we borrow the values, it can work when we own them.

AmigoNico
  • 6,652
  • 1
  • 35
  • 45

1 Answers1

7

Surely if this can work when we borrow the values, it can work when we own them.

No. A HashMap will move entries around in memory when it needs to resize itself to accommodate new entries. If the key borrows the value, and the entry is moved, then the address of the value will change and the reference in the key will no longer be valid.

For more information, see Why can't I store a value and a reference to that value in the same struct?.

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155