了解一生的问题
Understanding a lifetime issue
我在编译为 Firecracker 所做的更改时遇到了生命周期错误(在 aarch64
上,但我怀疑该问题是否与架构相关):
error[E0716]: temporary value dropped while borrowed
--> src/vmm/src/device_manager/mmio.rs:174:24
|
174 | let int_evt = &serial
| ________________________^
175 | | .lock()
176 | | .expect("Poisoned legacy serial lock")
| |__________________________________________________^ creates a temporary which is freed while still in use
177 | .interrupt_evt();
| - temporary value is freed at the end of this statement
178 | vm.register_irqfd(int_evt, self.irq)
| ------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
原始代码(编译良好)是:
vm.register_irqfd(&serial
.lock()
.expect("Poisoned legacy serial lock")
.interrupt_evt(), self.irq)
.map_err(Error::RegisterIrqFd)?;
我不明白其中的区别。错误消息似乎表明 expect()
正在返回一个临时文件并且我正在对它进行 const 引用,在 C++ 中这会延长临时文件的生命周期,不是在 Rust 中吗?无论哪种方式,为什么它在原始代码中有效,但在我绑定到左值后却无效(C++ 的说法,我不确定它是否与 Rust 相同)?
我尝试在此处创建 SSCE,但它按预期工作!
一个简单的、可重现的问题示例 (playground):
// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);
// get reference to item inside array
let item: &i32 = mutex.lock().unwrap().get(0).unwrap();
// use reference for something
println!("item = {:?}", item);
mutex.lock().unwrap()
returnsaMutexGuard<'_, Option<i32>>
,就是借用互斥锁里面的数据。它还拥有对数据的锁定,当保护被删除时释放,这意味着没有其他人可以同时借用数据。
当你在那个守卫上调用一个内部类型的方法时(比如上面例子中的.get
,或者你代码中的.interrupt_evt
),它将借用守卫的生命周期,因为你只能在守卫存在的情况下安全地访问数据。但是守卫没有存储在任何变量中,所以它只是暂时存在于该语句中,并在它结束时立即被删除。所以你不能得到语句外数据的引用。
解决这个问题很简单:先把守卫存到一个变量里,然后再借用它。这将确保守卫的寿命比您从中获得的参考更长(playground):
// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);
// get reference to item inside array
let guard = mutex.lock().unwrap();
let item: &i32 = guard.get(0).unwrap();
// use reference for something
println!("item = {:?}", item);
// guard is now destroyed at end of scope
// and mutex lock is released here
我在编译为 Firecracker 所做的更改时遇到了生命周期错误(在 aarch64
上,但我怀疑该问题是否与架构相关):
error[E0716]: temporary value dropped while borrowed
--> src/vmm/src/device_manager/mmio.rs:174:24
|
174 | let int_evt = &serial
| ________________________^
175 | | .lock()
176 | | .expect("Poisoned legacy serial lock")
| |__________________________________________________^ creates a temporary which is freed while still in use
177 | .interrupt_evt();
| - temporary value is freed at the end of this statement
178 | vm.register_irqfd(int_evt, self.irq)
| ------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
原始代码(编译良好)是:
vm.register_irqfd(&serial
.lock()
.expect("Poisoned legacy serial lock")
.interrupt_evt(), self.irq)
.map_err(Error::RegisterIrqFd)?;
我不明白其中的区别。错误消息似乎表明 expect()
正在返回一个临时文件并且我正在对它进行 const 引用,在 C++ 中这会延长临时文件的生命周期,不是在 Rust 中吗?无论哪种方式,为什么它在原始代码中有效,但在我绑定到左值后却无效(C++ 的说法,我不确定它是否与 Rust 相同)?
我尝试在此处创建 SSCE,但它按预期工作!
一个简单的、可重现的问题示例 (playground):
// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);
// get reference to item inside array
let item: &i32 = mutex.lock().unwrap().get(0).unwrap();
// use reference for something
println!("item = {:?}", item);
mutex.lock().unwrap()
returnsaMutexGuard<'_, Option<i32>>
,就是借用互斥锁里面的数据。它还拥有对数据的锁定,当保护被删除时释放,这意味着没有其他人可以同时借用数据。
当你在那个守卫上调用一个内部类型的方法时(比如上面例子中的.get
,或者你代码中的.interrupt_evt
),它将借用守卫的生命周期,因为你只能在守卫存在的情况下安全地访问数据。但是守卫没有存储在任何变量中,所以它只是暂时存在于该语句中,并在它结束时立即被删除。所以你不能得到语句外数据的引用。
解决这个问题很简单:先把守卫存到一个变量里,然后再借用它。这将确保守卫的寿命比您从中获得的参考更长(playground):
// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);
// get reference to item inside array
let guard = mutex.lock().unwrap();
let item: &i32 = guard.get(0).unwrap();
// use reference for something
println!("item = {:?}", item);
// guard is now destroyed at end of scope
// and mutex lock is released here