HashMap entry does not live long enouth to return the value

#1

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:

0 Likes

#2

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

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=585ed6227d35313a12b8b86b4cd56ba0

0 Likes

#3

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.

0 Likes

#4

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…

0 Likes

#5

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

#6

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…)

0 Likes

#7

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

0 Likes

#8

yay ! it’s working, thanks a lot !

1 Like