由于需求冲突,无法推断autoref的适当生命周期

Bra*_*lko 18 sdl lifetime rust

我在代码中遇到了特定功能的终生问题.我正在学习Rust和SDL的教程.该教程稍微陈旧,SDL库自编写以来已经发生了变化,所以我一直在跟进,同时也适应最新版本的Rust-SDL.

终身问题在于这个功能:

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)
    })
}
Run Code Online (Sandbox Code Playgroud)

尤其是线路self.ttf_context.load_font(Path::new(font_path), size as u16).ok().上面的注释行是旧的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>
Run Code Online (Sandbox Code Playgroud)

该实现的struct对象如下所示:

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>>
}
Run Code Online (Sandbox Code Playgroud)

该方法试图从Phi加载字体ttf_context并将其加载到hashmap中.Rust编译器建议我self在函数参数中添加一个生命周期,当我这样做时,会产生级联效果,为每个调用原始方法的方法添加生命周期,一直到main()没有任何帮助.

由于我还是Rust的新手,我不确定终身冲突所在的位置或者为什么会发生这种情况.作为猜测,我Font认为正在生成的对象应该随着该方法的结束而死亡,而是将其加载到具有生命周期'window和那两个冲突的hashmap中.但是,我对Rust没有足够的了解来解决这个问题,或者这是否正确.

She*_*ter 18

这是一个重现问题的小例子:

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() {}
Run Code Online (Sandbox Code Playgroud)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:20:32
   |
20 |         let font = self.loader.load();
   |                                ^^^^
   |
Run Code Online (Sandbox Code Playgroud)

问题确实是你构建了一个不可能的案例.具体来说,代码说明了这些要点:

  1. Phi将包括对a的引用Window.所提到的价值终生存在'window.
  2. Phi将包含一个Font包含引用的a.所提到的价值终生存在'window.
  3. FontLoader返回一个Font包含对带有加载器生存期的值的引用.这是由于终身推断,当扩展时看起来像:

    impl FontLoader {
        fn load<'a>(&'a self) -> Font<'a> {
            Font(&self.0)
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

然后代码尝试FontFontLoaderin中加载a Phi,它没有生命周期'window并将其存储FontPhi.FontLoader(并因此Font)活得不够长,所以不能存储Phi.

编译器已正确防止错误代码.


你的下一次尝试可能是引入第二次生命:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这实际上会编译,但可能不会做你想要的.请参阅为什么我不能在同一个结构中存储值和对该值的引用?了解更多信息.

更有可能的是,您想要引用字体加载器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我已经重命名了生命,因为它不再是严格的窗口了.

  • __FontLoader(以及 Font)的寿命不够长__:`FontLoader` 的生命周期与包含它的 Phi 的生命周期不一样吗? (4认同)
  • 那不是说它有“窗口”的生命周期吗?我不明白如果它的生命周期与 `Phi` 对象相同,因此与 `Phi.font` 相同,为什么它的寿命不够长?在什么情况下字体加载器会在“window”生命周期结束之前被释放? (4认同)
  • @MarioIshac,由于生命周期可以防止编译错误的代码,因此您需要使用原始指针来演示该问题。[这是一种这样的可能性](https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=2b5db621ea4921dcc1aa14b6733d2ab8)。请注意,指针现在指向无效位置。 (2认同)