你怎么能解决在Rust中不同匹配组中可变借用的需要?

时间:2017-03-26 00:07:45

标签: rust

在编译Piston游戏的这一部分时,我需要在不同的匹配臂中可变地借用一个对象。据我所知,它们都应该超出彼此的范围,但是会产生编译错误。

演示此问题的简化示例:

extern crate opengl_graphics;
extern crate piston_window;

use opengl_graphics::GlGraphics;
use opengl_graphics::glyph_cache::GlyphCache;
use piston_window::{Button, Context, Input, Key, OpenGL, PistonWindow, Size, text, WindowSettings};

struct Game<'a> {
    glyph_cache: &'a mut GlyphCache<'a>,
}

impl<'a> Game<'a> {
    pub fn new(glyph_cache: &'a mut GlyphCache<'a>) -> Self {
        Game { glyph_cache: glyph_cache }
    }
}

fn main() {
    let mut glyph_cache = GlyphCache::new("./FiraSans-Regular.ttf").unwrap();
    let opengl = OpenGL::V3_2;
    let mut window: PistonWindow = WindowSettings::new("Title",
                                                       Size {
                                                           width: 1024,
                                                           height: 768,
                                                       })
            .opengl(opengl)
            .build()
            .unwrap();
    let mut gl = GlGraphics::new(opengl);

    while let Some(event) = window.next() {
        match event {
            Input::Render(args) => {
                gl.draw(args.viewport(),
                        |context, graphics| draw(context, graphics, &mut glyph_cache));
            }

            Input::Press(Button::Keyboard(key)) => {
                match key {
                    Key::Space => {
                        Game::new(&mut glyph_cache);
                        // In real code, the `Game` instance is then used here,
                        // until going out of scope.
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

fn draw(context: Context, graphics: &mut GlGraphics, glyph_cache: &mut GlyphCache) {
    text([1.0, 1.0, 1.0, 1.0],
         72,
         "Hello, World!",
         glyph_cache,
         context.transform,
         graphics);
}

我认为当Game超出范围并被删除时,它将不再影响借用。从稳定的Rust 1.16.0生成的编译错误:

error[E0499]: cannot borrow `glyph_cache` as mutable more than once at a time
  --> src\main.rs:35:25
   |
35 |                         |context, graphics| draw(context, graphics, &mut glyph_cache));
   |                         ^^^^^^^^^^^^^^^^^^^                              ----------- borrow occurs due to use of `glyph_cache` in closure
   |                         |
   |                         second mutable borrow occurs here
...
41 |                         Game::new(&mut glyph_cache);
   |                                        ----------- first mutable borrow occurs here
...
49 | }
   | - first borrow ends here

error[E0499]: cannot borrow `glyph_cache` as mutable more than once at a time
  --> src\main.rs:41:40
   |
41 |                         Game::new(&mut glyph_cache);
   |                                        ^^^^^^^^^^^
   |                                        |
   |                                        second mutable borrow occurs here
   |                                        first mutable borrow occurs here
...
49 | }
   | - first borrow ends here

1 个答案:

答案 0 :(得分:1)

tl; dr 为您的包装器结构提供两种不同的生命周期:

struct Game<'a, 'b: 'a> {
    glyph_cache: &'a mut GlyphCache<'b>,
}

impl<'a, 'b> Game<'a, 'b> {
    pub fn new(glyph_cache: &'a mut GlyphCache<'b>) -> Self {
        Game { glyph_cache: glyph_cache }
    }
}

通过注释代码,删除包装代码,创建替换结构等,您的示例可以进一步最小化:

struct GlyphCache<'a>(&'a str);
struct Game<'a>(&'a mut GlyphCache<'a>);

fn main() {
    let mut glyph_cache = GlyphCache("dummy");
    loop {
        if true {
            &mut glyph_cache;
        } else {
            Game(&mut glyph_cache);
        }
    }
}

然后,您可以展开循环一次并使用内联条件来查找编译器抱怨的情况:

struct GlyphCache<'a>(&'a str);
struct Game<'a>(&'a mut GlyphCache<'a>);

fn main() {
    let mut glyph_cache = GlyphCache("dummy");

    Game(&mut glyph_cache);
    &mut glyph_cache;
}

有趣的是,a related question was asked in the last few daysWhy does linking lifetimes matter only with mutable references?也与此问题相关。这个彻底描述了原因,最终会导致终身差异(或者可能缺乏)。

根据我的理解(我可能不会),有两个组合:

  1. 当外部和内部生命周期相同时,Game构造函数可能会从GlyphCache获取引用的所有权。此检查仅在功能签名级别完成;未在main

  2. 中检查构造函数的主体的用法
  3. 生命周期目前是词汇。这意味着虽然结构立即被删除,但借用可能的借用的借入/再借入/转移持续时间更长,与循环的下一次迭代冲突。