web-dev-qa-db-fra.com

Impossible de déduire une durée de vie appropriée pour la réfection automatique en raison d'exigences contradictoires

J'ai des problèmes à vie avec une fonction particulière dans mon code. Je suis en train de suivre un tutoriel pour essayer d'apprendre Rust et SDL. Le tutoriel était légèrement plus ancien et la bibliothèque SDL a changé depuis qu'elle a été écrite, donc je suis tout en l'adaptant vers la dernière version de Rust-SDL.

Le problème de durée de vie est dans cette fonction:

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)
    })
}

en particulier avec la ligne self.ttf_context.load_font(Path::new(font_path), size as u16).ok(). La ligne commentée au-dessus est la méthode de chargement des polices de l'ancienne version SDL.

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>

L'objet struct pour cette implémentation ressemble à ceci:

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>>
}

La méthode essaie de charger une police à partir de ttf_context De Phi et de la charger dans la table de hachage. Le compilateur Rust a suggéré que j'ajoute une durée de vie à self dans les paramètres de la fonction, ce qui, lorsque je l'ai fait, a provoqué un effet en cascade pour ajouter des durées de vie à chaque méthode appelant l'original) , jusqu'à main() et n'a rien aidé.

Étant donné que je suis encore nouveau à Rust, je ne sais pas où réside le conflit à vie ni pourquoi cela se produit. En guise de supposition, je pense que l'objet Font qui est généré est censé mourir avec la fin de cette méthode, mais à la place, il est chargé dans une table de hachage avec une durée de vie de 'window Et ces deux conflits. Je n'en sais pas assez sur Rust pour résoudre ce problème, cependant, ou si c'est même correct.

20
Brad Ziolko

Voici un exemple plus petit qui reproduit le problème:

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();
   |                                ^^^^
   |

Le problème est en effet que vous avez construit un boîtier impossible. Plus précisément, le code énonce ces points:

  1. Phi va inclure une référence à un Window. Cela faisait référence à des vies pour la vie 'window.
  2. Phi va inclure un Font, qui contient une référence. Cela faisait référence à des vies pour la vie 'window.
  3. FontLoader renvoie un Font qui contient une référence à une valeur avec la durée de vie du chargeur . Cela est dû à l'inférence à vie, qui, lorsqu'elle est développée, ressemble à:

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

Ensuite, le code tente de charger un Font à partir du FontLoader dans Phi, ce que ne fait pas avoir la vie 'window et stockez ce Font dans Phi. FontLoader (et donc Font) ne vit pas assez longtemps, il ne peut donc pas être stocké dans Phi.

Le compilateur a correctement empêché un code incorrect.


Votre prochaine tentative serait probablement d'introduire une seconde vie:

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);
    }
}

Cela compilera en fait, mais ne fait probablement pas ce que vous voulez. Voir Pourquoi ne puis-je pas stocker une valeur et une référence à cette valeur dans la même structure? pour plus d'informations.

Plus probablement, vous voulez prendre une référence au chargeur de polices:

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);
    }
}

Ici, j'ai renommé la durée de vie car ce n'est plus strictement pour la fenêtre.

18
Shepmaster