【发布时间】:2019-07-19 22:44:26
【问题描述】:
这是Thing:
struct Thing(i32);
impl Thing {
pub fn increment_self(&mut self) {
self.0 += 1;
println!("incremented: {}", self.0);
}
}
下面的函数会尝试改变 Thing 并返回真或假,具体取决于 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 现在由 可变 引用传递,这不是我想要的:
- 我可以通过重新分配
None来改变Option,从而破坏所有后续的突变。 (例如取消注释第 12 行 ((*handle) = None;)。) - 一团糟:有很多无关的
&mut在撒谎。 - 真是一团糟:天知道为什么我必须在
if let语句中使用ref mut,而惯例是在其他任何地方使用&mut。 - 它违背了在编译器中设置复杂的借用检查和可变性检查规则的目的。
有什么方法可以真正实现我想要的:通过引用传递一个不可变的Option,并且实际上能够使用它的内容?
【问题讨论】:
-
"你如何使用一个不可变的 Option,通过引用,它包含一个可变引用?"你不能像编译器解释的那样“
t是一个&引用,所以它引用的数据不能被借用为可变的” -
@Stargateur - .Line 12 是
(*handle) = None; -
@Stargateur - 不过,我不会改变选项,因为它包含一个引用,我没有更改引用。在我的突变之后,引用仍然指向同一件事。我正在改变那东西内部的一些东西。
-
@Stargateur - 此外,使用
RefCell无法实现我想要的模式,因为我可以更改RefCell以完全包含不同的Thing- 与重新分配 @ 基本相同987654355@ 无。我可以在内部重写Thing以使用RefCell,但这意味着我必须重组我的整个库并在任何地方产生运行时借用检查的成本。
标签: reference rust immutability mutability