这是一个东西
:
struct Thing(i32);
impl Thing {
pub fn increment_self(&mut self) {
self.0 += 1;
println!("incremented: {}", self.0);
}
}
这里有一个函数尝试改变 Thing
并返回 true 或 false,具体取决于 Thing
是否可用:
fn try_increment(handle: Option<&mut Thing>) -> bool {
if let Some(t) = handle {
t.increment_self();
true
} else {
println!("warning: increment failed");
false
}
}
这是一个用法示例:
fn main() {
try_increment(None);
let mut thing = Thing(0);
try_increment(Some(&mut thing));
try_increment(Some(&mut thing));
try_increment(None);
}
如上所述,it works just fine (link to Rust playground) .输出如下:
warning: increment failed
incremented: 1
incremented: 2
warning: increment failed
当我想编写一个将 Thing
改变两次的函数时,问题就出现了。例如,以下内容不起作用:
fn try_increment_twice(handle: Option<&mut Thing>) {
try_increment(handle);
try_increment(handle);
}
fn main() {
try_increment_twice(None);
let mut thing = Thing(0);
try_increment_twice(Some(&mut thing));
try_increment_twice(None);
}
这个错误很有道理。第一次调用 try_increment(handle)
放弃了 handle
的所有权,因此第二次调用是非法的。通常情况下,Rust 编译器会产生一条合理的错误消息:
|
24 | try_increment(handle);
| ------ value moved here
25 | try_increment(handle);
| ^^^^^^ value used here after move
|
为了解决这个问题,我认为通过引用传递 handle
是有意义的。请记住,它应该是一个不可变 引用,因为我不希望try_increment
能够更改handle
本身(分配None
到它,例如)只能调用它的值的突变。
我的问题是我不知道该怎么做。
在这里is the closest working version that I could get :
struct Thing(i32);
impl Thing {
pub fn increment_self(&mut self) {
self.0 += 1;
println!("incremented: {}", self.0);
}
}
fn try_increment(handle: &mut Option<&mut Thing>) -> bool {
// PROBLEM: this line is allowed!
// (*handle) = None;
if let Some(ref mut t) = handle {
t.increment_self();
true
} else {
println!("warning: increment failed");
false
}
}
fn try_increment_twice(mut handle: Option<&mut Thing>) {
try_increment(&mut handle);
try_increment(&mut handle);
}
fn main() {
try_increment_twice(None);
let mut thing = Thing(0);
try_increment_twice(Some(&mut thing));
try_increment_twice(None);
}
此代码按预期运行,但 Option
现在由 mutable 引用传递,这不是我想要的:
- 我可以通过将
None
重新分配给它来改变Option
,从而破坏所有后续突变。 (取消注释第 12 行 ((*handle) = None;
) 例如。) - 很乱:到处都是大量无关的
&mut
。 - 这是双重困惑:天知道为什么我必须在
if let
语句中使用ref mut
而约定是在所有地方使用&mut
否则。 - 它违背了在编译器中使用复杂的借用检查和可变性检查规则的目的。
有没有什么方法可以真正实现我想要的:通过引用传递一个不可变的 Option
,并且实际上能够使用它的内容?
最佳答案
您不能从不可变引用中提取可变引用,即使是对其内部结构的引用。这就是重点!不可变引用的多个别名是允许的,因此,如果 Rust 允许您这样做,您可能会遇到两段代码能够同时改变相同数据的情况。
Rust 为 interior mutability 提供了几个逃生口,例如 RefCell
:
use std::cell::RefCell;
fn try_increment(handle: &Option<RefCell<Thing>>) -> bool {
if let Some(t) = handle {
t.borrow_mut().increment_self();
true
} else {
println!("warning: increment failed");
false
}
}
fn try_increment_twice(handle: Option<RefCell<Thing>>) {
try_increment(&handle);
try_increment(&handle);
}
fn main() {
let mut thing = RefCell::new(Thing(0));
try_increment_twice(Some(thing));
try_increment_twice(None);
}
关于reference - 如何通过引用使用包含可变引用的不可变选项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54884678/