由于需求冲突,无法为 autoref 推断适当的生命周期 [英] Cannot infer an appropriate lifetime for autoref due to conflicting requirements

查看:22
本文介绍了由于需求冲突,无法为 autoref 推断适当的生命周期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的代码中的某个特定函数存在生命周期问题.我正在学习 Rust 和 SDL 的教程.本教程稍早,并且 SDL 库自编写以来发生了变化,因此我正在跟进,同时还将其调整为最新版本的 Rust-SDL.

生命周期问题在这个函数中:

pub fn ttf_str_sprite(&mut self, text: &str, font_path: &'static str, size: i32, color: Color) ->选项<精灵>{if let Some(font) = self.cached_fonts.get(&(font_path, size)) {返回 font.render(text).blend(color).ok().and_then(|surface| self.renderer.create_texture_from_surface(&surface).ok()).map(精灵::新)}//::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(|字体| {self.cached_fonts.insert((font_path, size), font);self.ttf_str_sprite(文本,字体路径,大小,颜色)})}

特别是 self.ttf_context.load_font(Path::new(font_path), size as u16).ok().上面的注释行是旧SDL版本的字体加载方式.

error[E0495]: 由于需求冲突,无法为 autoref 推断合适的生命周期-->srcphi/mod.rs:57:26|57 |self.ttf_context.load_font(Path::new(font_path), size as u16).ok()|^^^^^^^^^^|帮助:考虑使用一个显式的生命周期参数,如下所示: fn ttf_str_sprite(&'window mut self, text: &str, font_path: &'static str,尺寸:i32,颜色:彩色)->选项<精灵>

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

pub struct Phi<'window>{酒吧活动:活动,酒吧渲染器:渲染器<'window>,发布 ttf_context: Sdl2TtfContext,cached_fonts: HashMap<(&'static str, i32), ::sdl2_ttf::Font<'window>>}

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

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

解决方案

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

struct FontLoader(String);struct Font<'a>(&'a str);impl FontLoader {fn load(&self) ->字体{字体(&self.0)}}结构体窗口;struct Phi<'window>{窗口:&'窗口窗口,加载器:FontLoader,字体:选项<字体<'窗口>>,}impl<'window>Phi<'窗口>{fn do_the_thing(&mut self) {让字体 = self.loader.load();self.font = Some(font);}}fn main() {}

error[E0495]: 由于需求冲突,无法为 autoref 推断合适的生命周期-->src/main.rs:20:32|20 |让字体 = self.loader.load();|^^^^|注意:首先,生命周期不能超过 19:5 方法主体上定义的匿名生命周期 #1 ...-->src/main.rs:19:5|19 |fn do_the_thing(&mut self) {|^^^^^^^^^^^^^^^^^^^^^^^^^^注意:...以便参考不会超过借用内容-->src/main.rs:20:20|20 |让字体 = self.loader.load();|^^^^^^^^^^^^注意:但是,生命周期必须对 18:6 impl 上定义的生命周期 `'window` 有效...-->src/main.rs:18:6|18 |impl<'window>Phi<'窗口>{|^^^^^^^^注意:...这样表达式是可赋值的-->src/main.rs:21:21|21 |self.font = Some(font);|^^^^^^^^^^^= 注意:预期的`Option>`找到了`Option`

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

  1. Phi 将包含对 Window 的引用.被引用的值在生命周期 'window 内有效.

  2. Phi 将包含一个 Font,其中包含一个引用.被引用的值在生命周期 'window 内有效.

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

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

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

然后代码尝试从 Phi 中的 FontLoader 加载 Font没有生命周期 'window 并将该 Font 存储到 Phi 中.FontLoader(因此Font)寿命不够长,所以它不能存储在Phi中.

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


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

struct Phi<'window, 'font>{窗口:&'窗口窗口,加载器:FontLoader,字体:选项<字体<'字体>>,}impl<'window, 'font>Phi<'window, 'font>{fn do_the_thing(&'font mut self) {让字体 = self.loader.load();self.font = Some(font);}}

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

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

struct Phi<'a>{窗口:&'一个窗口,loader: &'a FontLoader,字体:选项<字体<'a>>,}impl'a>Phia{fn do_the_thing(&mut self) {让字体 = self.loader.load();self.font = Some(font);}}

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

I'm having lifetime issues with a particular function in my code. I'm following a tutorial in an attempt to learn Rust and SDL. The tutorial was slightly older and the SDL library has changed since its been written, so I'm following along while also adapting it towards the latest version of Rust-SDL.

The lifetime problem is in this function:

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

particularly with the line self.ttf_context.load_font(Path::new(font_path), size as u16).ok(). The commented line above it is the old SDL version's font loading method.

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> srcphi/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>

The struct object for that implementation looks like this:

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

The method is trying to load a font from Phi's ttf_context and load it into the hashmap. The Rust compiler suggested I add a lifetime to self in the function parameters, which, when I did that, caused a cascading effect to adding lifetimes to every method calling the original one, all the way down to main() and didn't help anything.

Since I'm still new to Rust, I'm not sure where the lifetime conflict resides or why this is happening. As a guess, I'm thinking that the Font object that is being generated is supposed to die with the end of that method but instead it's being loaded into a hashmap with a lifetime of 'window and those two conflict. I don't know enough about Rust to fix that, though, or if that's even correct.

解决方案

Here's a smaller example that reproduces the problem:

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<'_>>`

The problem is indeed that you have constructed an impossible case. Specifically, the code states these points:

  1. Phi is going to include a reference to a Window. That referred-to value lives for the lifetime 'window.

  2. Phi is going to include a Font, which contains a reference. That referred-to value lives for the lifetime 'window.

  3. FontLoader returns a Font which contains a reference to a value with the lifetime of the loader. This is due to lifetime inference, which when expanded looks like:

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

    I highly encourage adding #![deny(rust_2018_idioms)] to your crate, which will disallow this specific type of lifetime inference.

Then the code attempts to load a Font from the FontLoader in Phi, which does not have the lifetime 'window and store that Font into Phi. FontLoader (and thus Font) does not live long enough, so it cannot be stored in Phi.

The compiler has correctly prevented incorrect code.


Your next attempt would probably be to introduce a second lifetime:

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

This will actually compile, but probably doesn't do what you want. See Why can't I store a value and a reference to that value in the same struct? for further information.

More likely, you want to take a reference to the font loader:

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

Here, I've renamed the lifetime as it isn't strictly for the window anymore.

这篇关于由于需求冲突,无法为 autoref 推断适当的生命周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆