rust sdl lifetime

rust - No se puede inferir una vida útil adecuada para la autorreferencia debido a requisitos en conflicto



sdl lifetime (1)

Tengo problemas de por vida con una función particular en mi código. Estoy siguiendo un tutorial en un intento de aprender Rust y SDL. El tutorial era un poco más antiguo y la biblioteca SDL ha cambiado desde que se escribió, por lo que lo sigo y también lo estoy adaptando a la última versión de Rust-SDL.

El problema de por vida está en esta función:

pub fn ttf_str_sprite(&mut self, text: &str, font_path: &''static str, size: i32, color: Color) -> Option<Sprite> { if let Some(font) = self.cached_fonts.get(&(font_path, size)) { return font.render(text).blended(color).ok() .and_then(|surface| self.renderer.create_texture_from_surface(&surface).ok()) .map(Sprite::new) } //::sdl2_ttf::Font::from_file(Path::new(font_path), size).ok() self.ttf_context.load_font(Path::new(font_path), size as u16).ok() .and_then(|font| { self.cached_fonts.insert((font_path, size), font); self.ttf_str_sprite(text, font_path, size, color) }) }

particularmente con la línea self.ttf_context.load_font(Path::new(font_path), size as u16).ok() . La línea comentada arriba es el método de carga de fuentes de la versión SDL anterior.

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements --> src/phi/mod.rs:57:26 | 57 | self.ttf_context.load_font(Path::new(font_path), size as u16).ok() | ^^^^^^^^^ | help: consider using an explicit lifetime parameter as shown: fn ttf_str_sprite(&''window mut self, text: &str, font_path: &''static str, size: i32, color: Color) -> Option<Sprite>

El objeto struct para esa implementación se ve así:

pub struct Phi<''window> { pub events: Events, pub renderer: Renderer<''window>, pub ttf_context: Sdl2TtfContext, cached_fonts: HashMap<(&''static str, i32), ::sdl2_ttf::Font<''window>> }

El método está tratando de cargar una fuente desde ttf_context de Phi y cargarla en el hashmap. El compilador de Rust sugirió que agregue una vida útil a mí self en los parámetros de la función, lo que, cuando hice eso, causó un efecto en cascada al agregar vidas a cada método que llama al original, todo el camino hasta main() y no ayudó cualquier cosa.

Como todavía soy nuevo en Rust, no estoy seguro de dónde reside el conflicto de por vida o por qué sucede esto. Como conjetura, estoy pensando que el objeto Font que se está generando se supone que muere con el final de ese método, pero en su lugar se está cargando en un mapa hash con una ''window de por vida de ''window y esos dos conflictos. Sin embargo, no sé lo suficiente sobre Rust para arreglar eso, o si eso es correcto.


Aquí hay un ejemplo más pequeño que reproduce el problema:

struct FontLoader(String); struct Font<''a>(&''a str); impl FontLoader { fn load(&self) -> Font { Font(&self.0) } } struct Window; struct Phi<''window> { window: &''window Window, loader: FontLoader, font: Option<Font<''window>>, } impl<''window> Phi<''window> { fn do_the_thing(&mut self) { let font = self.loader.load(); self.font = Some(font); } } fn main() {}

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements --> src/main.rs:20:32 | 20 | let font = self.loader.load(); | ^^^^ |

El problema es que has construido un caso imposible. Específicamente, el código establece estos puntos:

  1. Phi incluirá una referencia a una Window . Ese valor referido vive para la ''window toda la vida.
  2. Phi incluirá una Font , que contiene una referencia. Ese valor referido vive para la ''window toda la vida.
  3. FontLoader devuelve una Font que contiene una referencia a un valor con la vida útil del cargador . Esto se debe a la inferencia de por vida, que cuando se expande se ve así:

    impl FontLoader { fn load<''a>(&''a self) -> Font<''a> { Font(&self.0) } }

Luego, el código intenta cargar una Font del FontLoader en Phi , que no tiene la ''window vida y almacenar esa Font en Phi . FontLoader (y, por lo tanto, Font ) no vive lo suficiente, por lo que no se puede almacenar en Phi .

El compilador ha evitado correctamente el código incorrecto.

Su próximo intento probablemente sería introducir una segunda vida:

struct Phi<''window, ''font> { window: &''window Window, loader: FontLoader, font: Option<Font<''font>>, } impl<''window, ''font> Phi<''window, ''font> { fn do_the_thing(&''font mut self) { let font = self.loader.load(); self.font = Some(font); } }

Esto realmente se compilará, pero probablemente no haga lo que quieres. Consulte ¿Por qué no puedo almacenar un valor y una referencia a ese valor en la misma estructura? para mayor información.

Lo más probable es que desee tomar una referencia al cargador de fuentes:

struct Phi<''a> { window: &''a Window, loader: &''a FontLoader, font: Option<Font<''a>>, } impl<''a> Phi<''a> { fn do_the_thing(&mut self) { let font = self.loader.load(); self.font = Some(font); } }

Aquí, he cambiado el nombre de la vida ya que ya no es estrictamente para la ventana.