在编译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
答案 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 days和Why does linking lifetimes matter only with mutable references?也与此问题相关。这个彻底描述了原因,最终会导致终身差异(或者可能缺乏)。
根据我的理解(我可能不会),有两个组合:
当外部和内部生命周期相同时,Game
构造函数可能会从GlyphCache
获取引用的所有权。此检查仅在功能签名级别完成;未在main
生命周期目前是词汇。这意味着虽然结构立即被删除,但借用可能的借用的借入/再借入/转移持续时间更长,与循环的下一次迭代冲突。