我的代码中有一个特定函数存在终生问题。我正在学习一个教程,试图学习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的ttf_context加载字体并将其加载到hashmap中。Rust编译器建议我在函数参数中为self添加一个lifetime,当我这样做时,会对调用原始方法的每个方法添加lifetime产生级联效应,一直到main()都没有任何帮助。
因为我还没开始生锈,我不知道这一生的冲突在哪里,也不知道为什么会这样。作为猜测,我认为正在生成的Font对象应该随着该方法的结束而消亡,但是它被加载到一个生命周期为'window的hashmap中,并且这两个冲突。不过,我对铁锈的了解还不足以修复它,或者说这是否正确。

最佳答案

下面是一个小例子,它再现了这个问题:

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

问题是你确实构造了一个不可能的案例。具体而言,该准则规定了以下几点:
Phi将包含对aWindow的引用。指的是生命周期的价值。
'window将包含一个包含引用的Phi。指的是生命周期的价值。
Font返回一个'window,其中包含对加载程序生存期中某个值的引用。这是由于生命周期推断,当展开时,它看起来像:
impl FontLoader {
    fn load<'a>(&'a self) -> Font<'a> {
        Font(&self.0)
    }
}

然后代码尝试从FontLoader中的Font加载一个Font,它没有生存期FontLoader,并将该Phi存储到'windowFont(因此Phi)的寿命不够长,因此无法将其存储在FontLoader中。
编译器正确地防止了错误的代码。
你的下一个尝试可能是引入第二个生命:
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);
    }
}

在这里,我重新命名了生命,因为它不再是严格意义上的窗口。

关于rust - 由于需求冲突,无法推断自动引用的适当生命周期,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41527212/

10-13 02:22