error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements


#1

Hello,
I’ve got a problem with my game engine. I think it is due to the lifetime 'b but i don’t know how to make a correct lifetime in this case.

pub struct TextureManager<'a> {
    pub txt_map: HashMap<&'a str,piston_window::G2dTexture>
}

impl <'a> TextureManager<'a> {

    fn new(width: u32,height: u32) -> TextureManager<'a> {
        TextureManager{txt_map: HashMap::new()}
    }

    fn register(&mut self,window: &mut PistonWindow,name: &'a str) -> &piston_window::G2dTexture {
        self.txt_map.insert(name,piston_window::Texture::from_path(
            &mut window.factory,
            &find_folder::Search::ParentsThenKids(3, 3)
                .for_folder("assets").unwrap().join(name),
            piston_window::Flip::None,
            &piston_window::TextureSettings::new()
        ).unwrap());
        return self.get(name);
    }

    fn get(&self,stre: &'a str) -> &piston_window::G2dTexture {
        self.txt_map.get(stre).unwrap()
    }

    fn create<'b>(&mut self,txt_name: &'b str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'b,'a> {
        Sprite::new(self,txt_name,pos_x,pos_y,width,height)
    }
}

pub struct Sprite<'a,'b> {
    pub manager: &'b TextureManager<'b>,
    pub texture_name: &'a str,
    pub pos_x: f64,
    pub pos_y: f64,
    pub width: f64,
    pub height: f64,
    pub layer: u8
}

impl <'a,'b>Sprite<'a,'b> {

    pub fn new(manager: &'b mut TextureManager<'b>,texture_name: &'a str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'a,'b> {
        Sprite{manager,texture_name,pos_x,pos_y,width,height,layer: 0}
    }

    pub fn draw(&mut self,w: &mut piston_window::PistonWindow,e: &piston_window::Event) {
        w.draw_2d(e, |c, gl| {
            Image::new().rect([self.pos_x,self.pos_y,self.width,self.height])
                .draw(self.manager.get(self.texture_name), &piston_window::DrawState::new_alpha(), c.transform, gl);
        });
    }

    pub fn set_pos(&mut self,pos_x: f64,pos_y: f64) {
        self.pos_x=pos_x;
        self.pos_y=pos_y;
    }

    pub fn set_size(&mut self,width: f64,height: f64) {
        self.width=width;
        self.height=height;
    }
}

And the error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
   --> src/main.rs:137:9
    |
137 |         Sprite::new(self,txt_name,pos_x,pos_y,width,height)
    |         ^^^^^^^^^^^
    |

Thanks in advance and sorry for the inconvenience.
ccgauche.


#2

Debugging things is easier if you name your lifetimes consistently across all usage sites. For example fn create returns a Sprite<'b,'a>, but your struct definition is Sprite<'a,'b>. I have renamed the lifetimes in your code to be consistent for the following explanation.

Anyway, the issue is with the lifetime specification for &TextureManager. Sprite::new is

fn new(manager: &'tm mut TextureManager<'tm>,texture_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

But TextureManager::create is:

fn create<'sp>(&mut self,txt_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>

which is equivalent to:

fn create<'sp>(self: &mut Self,txt_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>

And Self is TextureManager<'tm>, so:

fn create<'sp>(self: &mut TextureManager<'tm>,txt_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>
                     ^^^^^^^^^^^^^^^^^^^^^^^^

This reveals the issue. You’re trying to pass a type &mut TextureManager<'tm> to a function expecting &'tm mut TextureManager<'tm>. The solution is to write TextureManager::create like so:

fn create<'sp>(&'tm mut self,txt_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>

#3

I’ve tried this but It didn’t work:


    fn create<'sp,'tm>(&'tm mut self,txt_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>  {
        Sprite::new(self,txt_name,pos_x,pos_y,width,height)
    }

If you can help me (You’ve learn me that a lifetime can have more than one character)


#4

I would try and avoid putting references in structs, if those strings are going to be string literals, then you can just use &'static str and you can use Rc/Arc instead of your other references. Swap back to references only id you find that this is the performance bottleneck.

Lifetimes get complicated very fast, so avoid explicut lifetimes as much as possible.


#5

It worked for me when I tried it. Please post your full code and error message.


#6

Actually, how come you’re putting a 'tm here: fn create<'sp, 'tm>? That shouldn’t be necessary, your impl should already define that lifetime.


#7
use std::collections::HashMap;


use opengl_graphics::{GlGraphics, OpenGL, Texture, TextureSettings};
use piston_window::*;

const layer_number: u8 = 3;

fn main() {
    unsafe {

        let mut window = create_window(1040,720);
        let mut manager = TextureManager::new(1040,720);
        manager.register(&mut window,"test.png");
        let mut scene = Scene::new();
        scene.add("test",&mut manager.create("test.png",0.0,0.0,100.0,100.0));
        while let Some(e) = window.next() {
            let instance = GraphicInstance::new(&e);
            instance.drawScene(&mut scene,&mut window);
        }
    }
}

fn create_window(width: u32,height: u32) -> PistonWindow {
    return
        WindowSettings::new("BurgerGrap - Beta 1.0", [width, height])
            .exit_on_esc(true)
            .opengl(OpenGL::V3_2)
            .vsync(true)
            .resizable(false)
            .decorated(true)
            .build()
            .unwrap();

}

pub struct Scene<'a, 'b, 'c> {
    sprites: HashMap<String,&'c mut Sprite<'a, 'b>>
}

impl <'a,'b,'c>Scene<'a,'b,'c> {

    fn new() -> Scene<'a,'b,'c> {
        Scene{sprites: HashMap::new()}
    }

    fn add(&mut self,nm: &str,sr: &'c mut Sprite<'a, 'b>) {
        self.sprites.insert(nm.to_string(),sr);
    }

    fn remove(&mut self,nm: &str) {
        self.sprites.remove(nm);
    }

    fn has(&mut self,nm: &str) -> bool {
        self.sprites.contains_key(nm)
    }

    fn set(&mut self,nm: &str,sr: &'c mut Sprite<'a, 'b>) {
        if self.has(nm) {
            self.remove(nm);
        }
        self.add(nm,sr);
    }

    fn get(&mut self,nm: &str) -> &Sprite {
        self.sprites.get(nm).unwrap()
    }
}

pub struct GraphicInstance<'a> {
    pub e: &'a piston_window::Event
}

impl <'a> GraphicInstance <'a> {

    pub fn new(e: &'a piston_window::Event) -> GraphicInstance<'a> {
        GraphicInstance{e}
    }

    pub fn key_event<T>(&self,t: T,k: Key)
        where T: Fn(){
        if let Some(b) = self.e.press_args() {
            if let Button::Keyboard(key) = b {
                if key == k {
                    t();
                }
            }
        }
    }

    pub fn drawScene(&self,fre: &mut Scene,window: &mut PistonWindow) {
        let mut j = layer_number+1;
        while j > 0 {
            j-=1;
            for (jd,jm) in &mut fre.sprites {
                if jm.layer==j {
                    jm.draw(window,self.e);
                }
            }
        }
    }

    pub fn drawSprites(&self,v: &mut Vec<Sprite>,window: &mut PistonWindow) {
        for e in v {
            e.draw(window,self.e);
        }
    }
}


pub struct TextureManager<'a> {
    pub txt_map: HashMap<&'a str,piston_window::G2dTexture>
}

impl <'a> TextureManager<'a> {

    fn new(width: u32,height: u32) -> TextureManager<'a> {
        TextureManager{txt_map: HashMap::new()}
    }

    fn register(&mut self,window: &mut PistonWindow,name: &'a str) -> &piston_window::G2dTexture {
        self.txt_map.insert(name,piston_window::Texture::from_path(
            &mut window.factory,
            &find_folder::Search::ParentsThenKids(3, 3)
                .for_folder("assets").unwrap().join(name),
            piston_window::Flip::None,
            &piston_window::TextureSettings::new()
        ).unwrap());
        return self.get(name);
    }

    fn get(&self,stre: &'a str) -> &piston_window::G2dTexture {
        self.txt_map.get(stre).unwrap()
    }

    fn create<'sp,'tm>(&'tm mut self,txt_name: &'sp str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'sp,'tm>  {
        Sprite::new(self,txt_name,pos_x,pos_y,width,height)
    }

}

pub struct Sprite<'a,'b> {
    pub manager: &'b TextureManager<'b>,
    pub texture_name: &'a str,
    pub pos_x: f64,
    pub pos_y: f64,
    pub width: f64,
    pub height: f64,
    pub layer: u8
}

impl <'a,'b>Sprite<'a,'b> {

    pub fn new(manager: &'b mut TextureManager<'b>,texture_name: &'a str,pos_x: f64,pos_y: f64,width: f64,height: f64) -> Sprite<'a,'b> {
        Sprite{manager,texture_name,pos_x,pos_y,width,height,layer: 0}
    }

    pub fn draw(&mut self,w: &mut piston_window::PistonWindow,e: &piston_window::Event) {
        w.draw_2d(e, |c, gl| {
            Image::new().rect([self.pos_x,self.pos_y,self.width,self.height])
                .draw(self.manager.get(self.texture_name), &piston_window::DrawState::new_alpha(), c.transform, gl);
        });
    }

    pub fn set_pos(&mut self,pos_x: f64,pos_y: f64) {
        self.pos_x=pos_x;
        self.pos_y=pos_y;
    }

    pub fn set_size(&mut self,width: f64,height: f64) {
        self.width=width;
        self.height=height;
    }
}

Here my code


#8

You probably want a structure like this (following the lifetime param renames that @jethrogb suggested - I second this when you start using more than 2 or 3 lifetime parameters, it helps keeping things straight in your mind):

// First, your `TextureManager` lifetime is renamed:
pub struct TextureManager<'tm> {
    pub txt_map: HashMap<&'tm str, piston_window::G2dTexture>
}

Then, you’ll probably want to introduce a 3rd lifetime parameter to Sprite:

// 'c is added, which allows us to borrow TextureManager for a shorter lifetime than its own,
// without requiring/depending on variance
pub struct Sprite<'a, 'b, 'c> {
    pub manager: &'b TextureManager<'c>,
    pub texture_name: &'a str,
    pub pos_x: f64,
    pub pos_y: f64,
    pub width: f64,
    pub height: f64,
    pub layer: u8,
}

Now modify create to (note 'tm is the TextureManager<'tm>, not a fresh lifetime parameter):

fn create<'slf, 'sp>(&'slf mut self, txt_name: &'sp str, pos_x: f64, pos_y: f64, width: f64, height: f64) -> Sprite<'sp, 'slf, 'tm> {
        Sprite::new(self, txt_name, pos_x, pos_y, width, height)
}

And finally modify new:

pub fn new(manager: &'b mut TextureManager<'c>, texture_name: &'a str, pos_x: f64, pos_y: f64, width: f64, height: f64) -> Sprite<'a, 'b, 'c> {
        Sprite { manager, texture_name, pos_x, pos_y, width, height, layer: 0 }
}

The 3rd lifetime, 'c, allows you to decouple the mutable borrow of TextureManager from its own lifetime ('tm).


#9

I worked but now I’ve got this error

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:16:31
   |
16 |         scene.add("test",&mut manager.create("test.png",0.0,0.0,100.0,100.0));
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                               |
   |                               creates a temporary which is freed while still in use
...
19 |             instance.drawScene(&mut scene,&mut window);
   |                                ---------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

warning: unnecessary `unsafe` block
  --> src/main.rs:10:5
   |
10 |     unsafe {
ccgauche@ccgauche-DevPC:~/IdeaProjects/GraisseEngine$ cargo run
   Compiling GraisseEngine v0.1.0 (/home/ccgauche/IdeaProjects/GraisseEngine)
warning: unused imports: `GlGraphics`, `TextureSettings`, `Texture`
 --> src/main.rs:4:23
  |
4 | use opengl_graphics::{GlGraphics, OpenGL, Texture, TextureSettings};
  |                       ^^^^^^^^^^          ^^^^^^^  ^^^^^^^^^^^^^^^
  |
  = note: #[warn(unused_imports)] on by default

warning: unused variable: `jd`
  --> src/main.rs:96:18
   |
96 |             for (jd,jm) in &mut fre.sprites {
   |                  ^^ help: consider using `_jd` instead
   |
   = note: #[warn(unused_variables)] on by default

warning: unused variable: `width`
   --> src/main.rs:118:12
    |
118 |     fn new(width: u32,height: u32) -> TextureManager<'a> {
    |            ^^^^^ help: consider using `_width` instead

warning: unused variable: `height`
   --> src/main.rs:118:23
    |
118 |     fn new(width: u32,height: u32) -> TextureManager<'a> {
    |                       ^^^^^^ help: consider using `_height` instead

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:16:31
   |
16 |         scene.add("test",&mut manager.create("test.png",0.0,0.0,100.0,100.0));
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                               |
   |                               creates a temporary which is freed while still in use
...
19 |             instance.drawScene(&mut scene,&mut window);
   |                                ---------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

And here the code

        scene.add("test",&mut manager.create("test.png",0.0,0.0,100.0,100.0));

ANd I don’t want to create a let statement


#10

Why not? You need to anchor the TextureManager so it stays alive. A simple:

...
let mut tm  = manager.create("test.png", 0.0, 0.0, 100.0, 100.0);
let mut scene = Scene::new();
scene.add("test", &mut tm);
...

should do the trick.


#11

Yes but my original idea is to simplify the code and limit number of line into the main


#12

Given you (seemingly) want to use references (and thus lifetime parameterized types), adding that single line feels pretty simple :man_shrugging: