【问题标题】:Cannot infer an appropriate lifetime for autoref due to conflicting requirements由于需求冲突,无法为 autoref 推断出合适的生命周期
【发布时间】:2017-05-07 07:21:13
【问题描述】:

我的代码中的特定函数存在生命周期问题。我正在学习一个教程,试图学习 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)
    })
}

尤其是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>

该实现的结构对象如下所示:

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

该方法正在尝试从 Phi 的 ttf_context 加载字体并将其加载到哈希图中。 Rust 编译器建议我在函数参数中为 self 添加生命周期,当我这样做时,会导致级联效应为每个调用原始方法的方法添加生命周期,一直到 main() 并且没有帮不上什么忙。

由于我还是 Rust 的新手,我不确定生命周期冲突在哪里或为什么会发生这种情况。作为一种猜测,我认为正在生成的 Font 对象应该随着该方法的结束而死,而是被加载到一个生命周期为 'window 的哈希图中,这两个冲突。不过,我对 Rust 的了解还不足以解决这个问题,或者这是否正确。

【问题讨论】:

    标签: rust sdl lifetime


    【解决方案1】:

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

    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();
       |                                ^^^^
       |
    note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 19:5...
      --> src/main.rs:19:5
       |
    19 |     fn do_the_thing(&mut self) {
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    note: ...so that reference does not outlive borrowed content
      --> src/main.rs:20:20
       |
    20 |         let font = self.loader.load();
       |                    ^^^^^^^^^^^
    note: but, the lifetime must be valid for the lifetime `'window` as defined on the impl at 18:6...
      --> src/main.rs:18:6
       |
    18 | impl<'window> Phi<'window> {
       |      ^^^^^^^
    note: ...so that the expression is assignable
      --> src/main.rs:21:21
       |
    21 |         self.font = Some(font);
       |                     ^^^^^^^^^^
       = note: expected `Option<Font<'window>>`
                  found `Option<Font<'_>>`
    

    问题确实是你构建了一个不可能的案例。具体来说,代码说明了以下几点:

    1. Phi 将包含对Window 的引用。所提到的价值终生存在'window

    2. Phi 将包含一个 Font,其中包含一个引用。所提到的价值终生存在'window

    3. FontLoader 返回一个 Font,其中包含对具有加载程序的生命周期的值的引用。这是由于生命周期推断,展开后看起来像:

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

      我强烈建议将 #![deny(rust_2018_idioms)] 添加到您的 crate 中,这将不允许这种特定类型的生命周期推断。

    然后代码尝试从Phi 中的FontLoader 加载Font,它没有具有'window 的生命周期,并将Font 存储到PhiFontLoader(因此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);
        }
    }
    

    这实际上会编译,但可能不会做你想要的。请参阅Why can't I store a value and a reference to that value in the same struct? 了解更多信息。

    更可能的是,您希望引用字体加载器:

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

    在这里,我重命名了生命周期,因为它不再严格用于窗口。

    【讨论】:

    • FontLoader(因此字体)的寿命不够长FontLoader 的寿命不与包含它的 Phi 相同吗?
    • @CarlLevasseur 是的,FontLoader 的生命周期与其包含的 Phi 相同。为什么要问?
    • 那不是说它有'window 的生命周期吗?如果它的生命周期与 Phi 对象相同,因此与 Phi.font 相同,我不明白为什么它的生命周期不够长?在什么情况下字体加载器会在 'window 生命周期结束之前被释放?
    • @CarlLevasseur 在什么情况下字体加载器会在'window 生命周期结束之前被释放——在每一种情况中,我相信。 这是否意味着它具有'window 生命周期 — 不。它包含一个具有window 生命周期的引用,但它本身具有不同的生命周期。
    • @MarioIshac 因为生命周期可以防止编译不正确的代码,所以您需要使用原始指针来演示问题。 Here's one such possibility。请注意,指针现在指向无效位置。
    猜你喜欢
    • 2021-08-02
    • 2016-06-01
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-29
    • 2016-05-21
    • 1970-01-01
    • 2022-01-06
    相关资源
    最近更新 更多