1

So I'm building a game engine in rust, but I have an issue with finding the correct lifetime parameters/syntax for storing the font data the sdl2 library needs for rendering.

This is a simplified version of what I'm currently doing, but it doesn't compile. As far as I can tell, the first lifetime parameter of the Font object given by ttf.load_font() must match the lifetime of the ttf object itself, but I can't come up with the correct syntax or data structures to tell the compiler about that lifetime relationship.

My current understanding is that ttf has the same (or shorter) lifetime as FontManager, being a data member, and any Font object created will have a lifetime tied to the ttf object that created it. So the <'a> in FontManager should be the same lifetime. There's obviously something I'm missing here, since it doesn't quite work, but I'm not sure what it is. If anyone could help explain what's going on, I would be immensely grateful.

mod sdl2;

struct FontManager<'a> {
  pub ttf: sdl2::ttf::Sdl2TtfContext,
  pub font: Option<sdl2::ttf::Font<'a, 'static>>,
}

impl<'a> FontManager<'a> {
  pub fn load_font(&'a mut self, path: &std::path::Path, size: usize) {
    match self.ttf.load_font(&self.ttf, path, size as u16) {
      Err(e) => panic!("{:?}", e),
      Ok(font) => { self.font = Some(font); }
    }
  }
}

fn main() -> Result<(), String> {
  let ttf = sdl2::ttf::init().map_err(|e| e.to_string())?;
  let mut fm = FontManager { ttf, font: None };
  fm.load_font(std::path::Path::new("Arial.ttf", 16); // <- error: 'fm' does not live long enough

  // ...main game loop...

} // <- error: 'fm' dropped here while still borrowed
SethSR
  • 59
  • 4
  • I don't immediately see a way to meaningfully use this library as is. Maybe the thing to do is to put the `Sdl2TtfContext` into a `Rc`, use `unsafe` code to turn the `&'a Sdl2TtfContext` into a `&'static Sdl2TtfContext`, and put the `Fontt<'static, 'static>` into a new struct that stores the `Font` alongside the `Rc`. You'll need a custom `Drop` implementation that drops the `Font` before the `Rc`. I don't quite understand `Pin` types here, and it seems like they might be useful in this context. – NovaDenizen Apr 08 '21 at 02:59
  • The lifetimes in this library are a bit wacky, they work more like C than Rust which will make them very difficult to use. The [signature for `Sdl2TtfContext::load_font`](https://docs.rs/sdl2/0.34.4/sdl2/ttf/struct.Sdl2TtfContext.html#method.load_font) specifies that the "`'ttf`" lifetime is the lifetime of the context itself. So, the context must outlive the font. In this scenario, probably the best solution is just to store the context in a static variable so that you can just get a `&'static` reference to it, since it seems you can only have one context at a time anyway. – Coder-256 Apr 08 '21 at 04:18
  • See [this answer](https://stackoverflow.com/a/27826181/3398839) for a rundown of different ways to get global/static variables. I think the best option here is `once_cell::unsync::Lazy`. IMO, its tiny, microscopic overhead is well worth the convenience (and safety/peace of mind). – Coder-256 Apr 08 '21 at 04:30

0 Answers0