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:
-
Phi
incluirá una referencia a unaWindow
. Ese valor referido vive para la''window
toda la vida. -
Phi
incluirá unaFont
, que contiene una referencia. Ese valor referido vive para la''window
toda la vida. -
FontLoader
devuelve unaFont
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.