reference - 如何通过引用使用包含可变引用的不可变选项?
问题描述
这是一个Thing
:
struct Thing(i32);
impl Thing {
pub fn increment_self(&mut self) {
self.0 += 1;
println!("incremented: {}", self.0);
}
}
这是一个尝试改变 aThing
并返回 true 或 false 的函数,具体取决于 aThing
是否可用:
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);
}
如上所述,它工作得很好(链接到 Rust 游乐场)。下面的输出:
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)
了away的所有权,handle
所以第二次调用是非法的。通常情况下,Rust 编译器会产生一个合理的错误消息:
|
24 | try_increment(handle);
| ------ value moved here
25 | try_increment(handle);
| ^^^^^^ value used here after move
|
为了解决这个问题,我认为handle
通过引用传递是有意义的。请注意,它应该是一个不可变的引用,因为我不希望try_increment
能够改变handle
自己(例如分配None
给它)只能调用它的值的突变。
我的问题是我无法弄清楚如何做到这一点。
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
现在通过可变引用传递,这不是我想要的:
- 我被允许
Option
通过重新分配它来改变None
它,打破所有后续的突变。(例如取消注释第 12 行 ((*handle) = None;
)。) - 很乱:有很多无关紧要
&mut
的谎言。 - 这是双重混乱:天堂只知道为什么我必须
ref mut
在if let
声明中使用,而约定是&mut
在其他任何地方使用。 - 它违背了在编译器中拥有复杂的借用检查和可变性检查规则的目的。
有什么方法可以真正实现我想要的:通过Option
引用传递一个不可变对象,并且实际上能够使用它的内容?
解决方案
您无法从不可变引用中提取可变引用,即使是对其内部的引用。这就是重点!不可变引用的多个别名是允许的,因此,如果 Rust 允许你这样做,你可能会遇到两段代码能够同时改变相同数据的情况。
Rust 为内部可变性提供了几个逃生舱口,例如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);
}
推荐阅读
- php - laravel 中所有路由的默认视图
- java - 使用 time square 获取两个选定的入住和退房日期
- postgresql - 如何通过添加所有可用行的日期来查询 PostgreSQL 以获取总时间?
- java - 计算Java句子中给定字母的出现次数
- cmake - CMake find_package:它在哪里找到包?
- html - 导航栏高度没有重新调整?
- java - 上传文件并将其编码为base64
- android - 为android客户端创建自定义openvpn以在TEE中生成私钥
- design-patterns - 命令模式 - 如何保持执行状态?
- mysql - 根据 MySQL 中的条件更新不同的字段