我的代码中的某个特定函数存在生命周期问题.我正在学习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>

该实现的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>>
}

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

由于我还是一个Rust 的新手,我不确定这一生的冲突在哪里,也不知道为什么会发生.作为猜测,我认为正在生成的Font个对象应该随着该方法的结束而消亡,但相反,它被加载到一个生存期为'window的hashmap中,这两个冲突.不过,我对Rust 的了解还不足以修复它,或者说这是否正确.

推荐答案

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

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

问题确实在于你已经构建了一个不可能的 case .具体而言,该准则规定了以下几点:

  1. Phi将包括对Window的引用.指的是生命的价值'window.

  2. Phi将包括一个Font,其中包含一个参考.指的是生命的价值'window.

  3. FontLoader返回一个Font,其中包含对具有生存期of the loader的值的引用.这是由于生命周期 推断,当展开时,它看起来像:

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

    我强烈建议在你的箱子里加#![deny(rust_2018_idioms)],这将不允许这种特定类型的终生推断.

然后,代码try 从Phi中的FontLoader加载一个Font,其中does not具有'window的生存期,并将该Font存储到Phi中.FontLoader(因此Font)生命周期 不够长,因此不能储存在Phi中.

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


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

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.

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

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

在这里,我重新命名了lifetime,因为它不再严格适用于窗口.

Rust相关问答推荐

Box::new()会从一个堆栈复制到另一个堆吗?

通过解引用将值移出Box(以及它被脱糖到什么地方)?

从Type::new()调用函数

如何定义实现同名但返回类型不同的 struct 的函数

无符号整数的Rust带符号差

如何实现Serde::Ser::Error的调试

程序在频道RX上挂起

为什么不';t(&;mut-iter).take(n)取得iter的所有权?

将特征与具有生命周期的关联类型一起使用时的生命周期方差问题

Rust 重写函数参数

Rust 中多个 & 符号的内存表示

Rust中的位移操作对范围有什么影响?

从 Rust 中的 if/else 中的引用创建 MappedRwLockWriteGuard

Rust Serde 为 Option:: 创建反序列化器

使用 rust 在 google cloud run (docker) 中访问环境变量的适当方法

Rust,使用枚举从 HashMap 获取值

将文件的第一行分别读取到文件的其余部分的最有效方法是什么?

Rust - 在线程之间不安全地共享没有互斥量的可变数据

您如何使用枚举反序列化字符串,其中任何其他值反序列化为新类型变体同时保留字符串?

如何用另一个变量向量置换 rust simd 向量?