1

I'm going in circles trying to figure out how to properly manage the setup of textures. I'm using https://github.com/Rust-SDL2/rust-sdl2/. I've tired to extract out a minimal example from my whole codebase, so hopefully I haven't missed any details in the translation. This is a simple game FYI.

What I'm trying to do is store a TextureCreator[1] and Texture[2] in the same struct. What's happening here is SDL makes TextureCreator the owner of every Texture created with it, which means I have to use explicit lifetimes. I've got it setup so that I can create a new Engine instance with the 'a lifetime for the ResMan struct. But as soon as I define and call the res_man.load_texture method, things go bonkers and I haven't been able to figure out how to properly do it in a way the borrowchecker doesn't make me run around in circles. You will notice I used a RefCell to try and attempt to fix it, but the borrowchecker seems to want some explicit lifetimes specified in the load_texture function.

I'm open to scrapping all of this for something more rustic if you can enlighten me.

use std::cell::RefCell;
use std::collections::HashMap;

use sdl2::image::{LoadTexture};
use sdl2::render::{Canvas, Texture, TextureCreator};
use sdl2::video::{Window, WindowContext};
use sdl2::{Sdl, VideoSubsystem};

const BLOCK_TEXTURE_PATH: &str = "../assets/block.png";

fn main() {
    let engine = Engine::new();
    engine.start();
}

pub struct ResMan<'a> {
    texture_creator: RefCell<TextureCreator<WindowContext>>,
    res_map: HashMap<&'static str, Texture<'a>>,
}

impl<'a> ResMan<'a> {
    pub fn new(canvas: &Canvas<Window>) -> ResMan<'a> {

        ResMan {
            texture_creator: RefCell::new(canvas.texture_creator()),
            res_map: HashMap::with_capacity(10),
        }
    }

    // here's horrible code that's on fire,   
    pub fn load_texture(&self, path: &'static str) {
        let texture_creator = self.texture_creator.borrow_mut();
        let block_texture = texture_creator.load_texture(path).unwrap();
        self.res_map.insert(path, block_texture);
    }
}

pub struct Engine<'a> {
    sdl_context: Sdl,
    video_subsystem: VideoSubsystem,
    canvas: Canvas<Window>,
    res_man: ResMan<'a>,
}

impl<'a> Engine<'a> {
    pub fn new() -> Engine<'a> {
        let sdl_context = sdl2::init().unwrap();
        let video_subsystem = sdl_context.video().unwrap();
        let window = Engine::open_window(&video_subsystem).unwrap();
        let canvas = Engine::create_canvas(window).unwrap();
        let mut res_man = ResMan::new(&canvas);
        res_man.load_texture(BLOCK_TEXTURE_PATH);

        Engine {
            sdl_context,
            video_subsystem,
            canvas,
            res_man,
        }
    }

    fn open_window(video_subsystem: &VideoSubsystem) -> Result<Window, String> {
        video_subsystem
            .window("rust-sdl2 demo", 10 * 32, 20 * 32)
            .position_centered()
            .build()
            .map_err(|e| e.to_string())
    }

    fn create_canvas(window: Window) -> Result<Canvas<Window>, String> {
        window
            .into_canvas()
            .accelerated()
            .build()
            .map_err(|e| e.to_string())
    }

    pub fn start(&self) {
        // main loop
    }
}

1: https://docs.rs/sdl2/0.32.2/sdl2/render/struct.TextureCreator.html

2: https://docs.rs/sdl2/0.32.2/sdl2/render/struct.Texture.html


Error message,

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:31:52
   |
31 |         let texture_creator = self.texture_creator.borrow_mut();
   |                                                    ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 30:5...
  --> src/main.rs:30:5
   |
30 | /     pub fn load_texture(&self, path: &'static str) {
31 | |         let texture_creator = self.texture_creator.borrow_mut();
32 | |         let block_texture = texture_creator.load_texture(path).unwrap();
33 | |         self.res_map.insert(path, block_texture);
34 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:31:31
   |
31 |         let texture_creator = self.texture_creator.borrow_mut();
   |                               ^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 21:6...
  --> src/main.rs:21:6
   |
21 | impl<'a> ResMan<'a> {
   |      ^^
   = note: ...so that the expression is assignable:
           expected sdl2::render::Texture<'a>
              found sdl2::render::Texture<'_>

error: aborting due to previous error

error: Could not compile `ffs`.
francium
  • 2,384
  • 3
  • 20
  • 29
  • your code don't make any sense, you have a infinite recursive call. – Stargateur Aug 05 '19 at 22:47
  • @Stargateur Must be a typo, I was not getting any such issues in my real code. Where is the recursive call? Do note that `texture_creator.load_texture` is different from `res_man.load_texture`. – francium Aug 05 '19 at 23:36
  • 1
    See also [Is there a way to store a texture inside a struct using rust-sdl2?](https://stackoverflow.com/q/56051593/155423); [Rust - cannot infer an appropriate lifetime for autoref due to conflicting requirements](https://stackoverflow.com/q/56080861/155423). – Shepmaster Aug 06 '19 at 00:12

0 Answers0