这里是Thing
:
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 引用传递了,这不是我想要的 :
Option
重新分配None
来突变(*handle) = None;
,打破随后的所有突变。 (例如,取消注释第12行(&mut
)。ref mut
。if let
语句中使用&mut
,而惯例是在其他任何地方都使用Option
。有什么方法可以真正实现我想要的:通过引用传递不可变的<input type="radio" name="user" value="1" data-active="0"> User 1
<input type="radio" name="user" value="2" data-active="0"> User 2
并真正能够使用其内容?
答案 0 :(得分:3)
您不能从不可变的引用中提取可变的引用,即使是对其内部的引用。这就是重点!允许使用不可变引用的多个别名,因此,如果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);
}
答案 1 :(得分:1)
TL; DR:答案是否,我不能。
与@Peter Hall和@Stargateur讨论之后,我开始理解为什么我需要在各处使用&mut Option<&mut Thing>
。 RefCell<>
也是可行的解决方法,但它不会整洁,也无法真正实现我最初试图实现的模式。
问题是这样的:如果一个人被允许变异一个对象,而该对象仅具有对Option<&mut T>
的 immutable 引用,则可以使用此功能完全打破借用规则。具体而言,从本质上讲,您可以对同一个对象有许多 可变 引用,因为您可以有许多这样的不可变引用。
我知道,只有一个 mutable 引用Thing
(归Option<>
拥有),但是,一旦我开始服用引用Option<>
以后,编译器不再知道其中没有很多。
该模式的最佳版本如下:
fn try_increment(handle: &mut Option<&mut Thing>) -> bool {
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<>
拥有对Thing
的唯一现存的可变引用try_increment_twice()
拥有Option<>
try_increment()
必须将Option<>
设为&mut
,以便编译器知道在此期间,它具有对Option<>
的唯一可变引用。通话try_increment()
是对Option<>
的唯一可变引用,而该引用具有对Thing
的唯一可变引用,则编译器会知道没有违反借用规则。 Option<>
的可变性问题仍然存在,因为人们可以打电话给take()
等。在易变的Option<>
上,破坏了随后的一切。
要实现我想要的模式,我需要类似于{em> Option<>
的东西,但是即使它是 mutable ,也不能对其进行突变。像这样:
struct Handle<'a> {
value: Option<&'a mut Thing>,
}
impl<'a> Handle<'a> {
fn new(value: &'a mut Thing) -> Self {
Self {
value: Some(value),
}
}
fn empty() -> Self {
Self {
value: None,
}
}
fn try_mutate<T, F: Fn(&mut Thing) -> T>(&mut self, mutation: F) -> Option<T> {
if let Some(ref mut v) = self.value {
Some(mutation(v))
}
else {
None
}
}
}
现在,我想,我可以整天绕过&mut Handle
,并且知道拥有Handle
的人只能更改其内容,而不能更改其自身。 (See Playground)
不幸的是,即使这样也没有任何好处,因为,如果您有可变的引用,则始终可以使用解引用运算符重新分配它:
fn try_increment(handle: &mut Handle) -> bool {
if let Some(_) = handle.try_mutate(|t| { t.increment_self() }) {
// This breaks future calls:
(*handle) = Handle::empty();
true
}
else {
println!("warning: increment failed");
false
}
}
一切都很好。
底线结论:只需使用&mut Option<&mut T>