此问题已在此处有答案:
What are non-lexical lifetimes?(1个答案)
10天前关门了。
以下代码无法编译(如预期):
use sdl2::event::Event;
use sdl2::pixels::Color;
pub fn main() {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("rust-sdl2 demo", 800, 600)
.build()
.unwrap();
let mut canvas = window.into_canvas().build().unwrap();
canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear();
canvas.present();
let mut event_pump = sdl_context.event_pump().unwrap();
let keyboard = sdl2::keyboard::KeyboardState::new(&event_pump);
'running: loop {
if keyboard.is_scancode_pressed(sdl2::keyboard::Scancode::Space) {
println!("test");
}
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } => break 'running,
_ => {}
}
}
}
}
字符串
编译器显示以下错误消息:
error[E0502]: cannot borrow `event_pump` as mutable because it is also borrowed as immutable
--> src\main.rs:25:22
|
19 | let keyboard = sdl2::keyboard::KeyboardState::new(&event_pump);
| ----------- immutable borrow occurs here
...
22 | if keyboard.is_scancode_pressed(sdl2::keyboard::Scancode::Space) {
| ------------------------------------------------------------- immutable borrow later used here
...
25 | for event in event_pump.poll_iter() {
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
型
如果我把let keyboard
行移到循环中,为什么它会编译?我想这可能与物体的寿命有关。但它显示的错误对我来说没有意义。根据我的理解,我不明白let keyboard
在循环外部还是内部有什么关系。
1条答案
按热度按时间46scxncf1#
Rust最好尝试看看它是否能找到一种方法来为您确定对象的范围,以便在函数体中不违反生存期。在循环中包含let keyboard时,Rust会查看它可以通过哪些有效方式来控制拖放顺序,并发现如下可能性:
字符串
换句话说,它注意到,如果你每次通过循环调用let,你每次都在有效地丢弃和重新分配键盘。然后,它继续注意到,如果它命令drop在你稍后去改变引用所指向的内容之前,去掉存储在键盘内部的对事件循环的不可变引用,那么它在语义上等同于你所写的内容。这样可以满足引用的要求,并且不会与您编写的实现冲突,因此编译器会自动替换它,以便您的代码可以编译,但它仍然是完全内存安全的。
这使得让let在循环之外的问题变得明显,因为keyboard都是1。不丢弃和2。在内部存储了对event_pump的不可变引用,因此它无法插入任何可能的删除顺序来确保内存安全,因为不再有删除操作。keyboard::new(约定)的函数签名意味着只要keyboard保持活动,就不能对event_pump进行变异。
如果在/match的事件之后再次使用keyboard,那么它将失败,因为它需要保持不可变的引用。
因此,基本上,编译器将“人工”作用域放置在有意义的地方,以便能够满足自己的规则,这样程序员就不必手动操作了。
只要可观察到的结果与您所写的完全等价,Rust就可以对操作重新排序,几乎所有的编译语言都是这样做的。操作之间的延迟不被认为是“可观察到的”,因此释放通常是允许移动的。这是允许人工作用域工作的基础属性。
作为一个历史记录,2015年 rust 没有这样做。Rust的最初版本使用了“词法生存期”借用检查器,但它不具备此功能。借用检查器在2018年版Rust中得到了“非词法生存期”检查器的大幅升级,不再严格依赖于标记的词法顺序,因此它可以进行这种类型的替换。