web-dev-qa-db-ja.com

要件の競合により、自動参照の適切な有効期間を推測できません

私のコードの特定の関数で生涯の問題があります。 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_のハッシュマップにロードされ、 2つの対立。 Rustについては、それを修正するために、またはそれが正しいかどうかについて、十分に知りません。

20
Brad Ziolko

この問題を再現する小さな例を次に示します。

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

問題は確かにあなたが不可能なケースを構築したことです。具体的には、コードは次の点を述べています。

  1. Phiには、Windowへの参照が含まれます。その参照された値は、ライフタイム'window
  2. Phiには、参照を含むFontが含まれます。その参照された値は、ライフタイム'window
  3. FontLoaderは、ローダーのライフタイムを持つ値への参照を含むFontを返します。これは、展開すると次のように見える寿命の推論によるものです。

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

次に、コードはFontからFontLoaderからPhiをロードしようとしますが、はロードしません存続期間'windowFontPhiに保存します。 FontLoader(したがってFont)は十分に長持ちしないため、Phiに格納できません。

コンパイラは正しくないコードを正しく防止しました。


次の試みは、おそらく2番目のライフタイムを導入することです。

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

これは実際にコンパイルされますが、おそらくあなたが望むことをしません。詳細については、 値とその値への参照を同じ構造体に格納できないのはなぜですか? を参照してください。

より可能性が高いのは、フォントローダーへの参照を取得することです。

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

ここでは、ウィンドウに限定されなくなったため、ライフタイムの名前を変更しました。

18
Shepmaster