HashMap entry does not live long enouth to return the value

Hi there!

I'm having trouble trying to get or insert a value in a HashMap:

pub struct Renderer {
    shaders: HashMap<String, Box<Shader>>, // Shader being a trait
}

impl Renderer {
    pub fn render() -> Result<(), Box<Error>> {
        let shader = match self.shaders.entry(material.name.clone()) {
            Entry::Occupied(entry) => entry.get(),
            Entry::Vacant(entry) => {
                let shader = Shader::try_from_material(context, material)?;
                entry.insert(shader)
            }
        };
    }
}

Note the ? inside the match, so I can't use the .or_insert entry method.

I'm getting the following error:

error[E0597]: `entry` does not live long enough
  --> game/src/webgl_gltf/mod.rs:39:39
   |
38 |         let shader = match self.shaders.entry(material.name.clone()) {
   |             ------ borrow later stored here
39 |             Entry::Occupied(entry) => entry.get(),
   |                                       ^^^^^ borrowed value does not live long enough
...
44 |         };
   |         - `entry` dropped here while still borrowed

The error is kinda explicit, but I can't manage to get my head around the problem.

How would you get or insert a value in a HashMap, considering the insert may fail ?

Thanks!

Edit: I got one previous version working by using RC instead of Box, but Rc feel kind of unnecessary here :confused:

One thing to note before the answer, please use the syntax dyn Trait for dynamically dispatched traits. For example Box<dyn Shader> and Box<dyn Error>. This makes it clear that Shader is a trait that is being dynamically dispatched.

Also I think you didn't put the full signature of Renderer::render


From your code I think you meant to put

impl Renderer {
    pub fn render(&mut self) -> Result<&String, Box<Error>> {
        let shader = match self.shaders.entry(material.name.clone()) {
            Entry::Occupied(entry) => entry.get(),
            Entry::Vacant(entry) => {
                let shader = Shader::try_from_material(context, material)?;
                entry.insert(shader)
            }
        };
    }
}

If so, then it would be sufficient to just clone the String.


My bad I misread what was happening,

try this instead

If you want shader to live only for the duration of the render() function, you should be able to fix the error in the most straight-forward way – by making entry live long enough:

pub fn render() -> Result<(), Box<dyn Error>> {
    let entry = self.shaders.entry(&material.name)
    let shader = match entry { … };
}

This ensures that entry outlives shader, so it should fix the error.

Thanks for your answer, but this doesn't works either:

let entry = self.shaders.entry(material.name.clone());
let shader = match entry {
    Entry::Occupied(occupied_entry) => occupied_entry.get(),
    Entry::Vacant(vacant_entry) => {
        let shader = Shader::try_from_material(context, material)?;
        vacant_entry.insert(shader)
    }
};
error[E0597]: `occupied_entry` does not live long enough
  --> game/src/webgl_gltf/mod.rs:40:52
   |
39 |             let shader = match entry {
   |                 ------ borrow later stored here
40 |                 Entry::Occupied(occupied_entry) => occupied_entry.get(),
   |                                                    ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
45 |             };
   |             - `occupied_entry` dropped here while still borrowed

It's the matched OccupiedEntry that does not live long enouth...

Instead of using occupied_entry.get() use occupied_entry.into_mut().

The difference is that get borrows the entry, so it must be outlived by the entry, but into_mut consumes the entry and gives you something you can use later.

1 Like

Thanks, it looks like the way to go, now i'm getting another error

let shader = match self.shaders.entry(material.name.clone()) {
    Entry::Occupied(entry) => Ok(&**entry.into_mut()),
    Entry::Vacant(entry) => {
        let shader = Shader::try_from_material(context, material)?;
        Ok(&**entry.insert(shader))
    }
};
  --> game/src/webgl_gltf/mod.rs:39:43
   |
38 |             let shader = match self.shaders.entry(material.name.clone()) {
   |                 ------ consider giving `shader` a type
39 |                 Entry::Occupied(entry) => Ok(&**entry.into_mut()),
   |                                           ^^ cannot infer type for `E`

And I have no idea where this E comes from (not from my code for sure...)

Ok, just take out the Ok(...) . I thought you were going to return them from the function so I wrapped them.

yay ! it's working, thanks a lot !

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.