函数参数中的可变借用
Mutable borrow in function argument
为什么以下代码无法编译 (playground):
use std::collections::HashMap;
fn main() {
let mut h: HashMap<u32, u32> = HashMap::new();
h.insert(0, 0);
h.insert(1, h.remove(&0).unwrap());
}
借款检查员抱怨说:
error[E0499]: cannot borrow `h` as mutable more than once at a time
--> src/main.rs:6:17
|
6 | h.insert(1, h.remove(&0).unwrap());
| - ------ ^ second mutable borrow occurs here
| | |
| | first borrow later used by call
| first mutable borrow occurs here
然而,代码是安全的,最后一行几乎是机械的转换使其可以编译 (playground):
//h.insert(1, h.remove(&0).unwrap());
let x = h.remove(&0).unwrap();
h.insert(1, x);
据我了解,此类问题已通过非词汇生命周期得到解决。 是一个例子,还有很多。
是否有一些微妙之处使第一个变体毕竟不正确,所以 Rust 拒绝它是正确的?还是 NLL 功能还没有在所有情况下完成?
Rust 编译器首先评估调用对象,然后评估传递给它的参数。这就是为什么它首先借用h.insert
,然后是h.remove
。由于 h
已经为 insert
可变借用,它拒绝为 remove
.
的第二次借用
使用下一代借阅检查器Polonius时,这种情况没有改变。您可以使用 nightly 编译器自己尝试一下:
RUSTFLAGS=-Zpolonius cargo +nightly run
求值顺序与 C++ 类似:https://riptutorial.com/cplusplus/example/19369/evaluation-order-of-function-arguments
你的问题也适用于一个可能更令人惊讶的相关案例——当方法参数需要 &mut self
:
时,方法调用需要 &self
use std::collections::HashMap;
fn main() {
let mut h: HashMap<u32, u32> = HashMap::new();
h.insert(0, 0);
h.contains_key(&h.remove(&0).unwrap());
}
Rust 借用检查器使用它所谓的两阶段借用。 chat I had with Niko Matsakis:
的编辑转录
The idea of two-phase borrows is that the outer &mut
is treated like an &
borrow until it is actually used, more or less. This makes it compatible with an inner &
because two &
mix, but it is not compatible with an inner &mut
.
If we wanted to support that, we'd have had to add a new kind of borrow -- i.e., an "unactivated" &mut
wouldn't act like an &
, it would act like something else (&const
, maybe... "somebody else can mutate")
It's less clear that this is OK and it seemed to add more concepts so we opted not to support it.
正如你所说,这是安全的,因为内部借用在外部借用开始之前完成,但实际上认识到此时在编译器中过于复杂。
另请参阅:
为什么以下代码无法编译 (playground):
use std::collections::HashMap;
fn main() {
let mut h: HashMap<u32, u32> = HashMap::new();
h.insert(0, 0);
h.insert(1, h.remove(&0).unwrap());
}
借款检查员抱怨说:
error[E0499]: cannot borrow `h` as mutable more than once at a time
--> src/main.rs:6:17
|
6 | h.insert(1, h.remove(&0).unwrap());
| - ------ ^ second mutable borrow occurs here
| | |
| | first borrow later used by call
| first mutable borrow occurs here
然而,代码是安全的,最后一行几乎是机械的转换使其可以编译 (playground):
//h.insert(1, h.remove(&0).unwrap());
let x = h.remove(&0).unwrap();
h.insert(1, x);
据我了解,此类问题已通过非词汇生命周期得到解决。
是否有一些微妙之处使第一个变体毕竟不正确,所以 Rust 拒绝它是正确的?还是 NLL 功能还没有在所有情况下完成?
Rust 编译器首先评估调用对象,然后评估传递给它的参数。这就是为什么它首先借用h.insert
,然后是h.remove
。由于 h
已经为 insert
可变借用,它拒绝为 remove
.
使用下一代借阅检查器Polonius时,这种情况没有改变。您可以使用 nightly 编译器自己尝试一下:
RUSTFLAGS=-Zpolonius cargo +nightly run
求值顺序与 C++ 类似:https://riptutorial.com/cplusplus/example/19369/evaluation-order-of-function-arguments
你的问题也适用于一个可能更令人惊讶的相关案例——当方法参数需要 &mut self
:
&self
use std::collections::HashMap;
fn main() {
let mut h: HashMap<u32, u32> = HashMap::new();
h.insert(0, 0);
h.contains_key(&h.remove(&0).unwrap());
}
Rust 借用检查器使用它所谓的两阶段借用。 chat I had with Niko Matsakis:
的编辑转录The idea of two-phase borrows is that the outer
&mut
is treated like an&
borrow until it is actually used, more or less. This makes it compatible with an inner&
because two&
mix, but it is not compatible with an inner&mut
.If we wanted to support that, we'd have had to add a new kind of borrow -- i.e., an "unactivated"
&mut
wouldn't act like an&
, it would act like something else (&const
, maybe... "somebody else can mutate")It's less clear that this is OK and it seemed to add more concepts so we opted not to support it.
正如你所说,这是安全的,因为内部借用在外部借用开始之前完成,但实际上认识到此时在编译器中过于复杂。
另请参阅: