我是 Rustacean 的新手(完成了 rustacean 课程,到目前为止非常享受)。我在 Rust 书中的一些闭包示例上遇到了麻烦。特别是这个:
fn main() {
let mut list = vec![1, 2, 3];
let mut borrows_mutably = || list.push(7);
borrows_mutably();
}
我们需要将关闭声明为
mut
这一事实对我来说很难理解。而且确实需要它,因为如果您不将其声明为 mut,编译器会抱怨:
calling `borrows_mutably` requires mutable binding due to mutable borrow of `list`
| |
| help: consider changing this to be mutable: `mut borrows_mutably`
|
| borrows_mutably();
| ^^^^ cannot borrow as mutable
我在这里和那里阅读了很多帖子,我的理解是需要将闭包定义为
mut
,因为它正在改变其环境。不知道这个直觉对不对……
无论如何,玩另一个闭包我发现了一个打破我理解的例子。这个:
fn main() {
let mut num_ops = 0;
let func = |r: &i32| {
num_ops += 1;
r.abs()
};
let mut list = [-3, 1, 5];
list.sort_by_key(func);
println!("{:?}, {}", list, num_ops);
}
我有一个闭包,用于按绝对值对整数列表进行排序,并计算发生的操作次数。显然,这个闭包也在改变它的环境。但是在这种情况下我不需要将其声明为
mut
,因为看起来我没有在我的代码中调用闭包,而是将它传递给另一个函数(sort_by_key
),而这个函数又会调用它.为什么会有这种行为?
我觉得它超级混乱。事实上,如果我没有将函数传递给
sort_by_key
而是直接调用它,我会得到同样的错误(cannot borrow func as mutable...
)。
我在这里和那里阅读了很多帖子,我的理解是需要将闭包定义为
,因为它正在改变其环境。不知道这个直觉对不对……mut
是的,这是正确的。在引擎盖下,闭包值捕获对
list
的可变引用。换句话说,borrows_mutably
值包含一个 &mut Vec<_>
并且正是通过这个引用调用了 Vec::push
。
那么为什么
borrows_mutably
需要声明mut
?首先,请注意通过共享引用访问可变引用不允许可变访问。这就是为什么 FnMut
闭包的调用签名需要 &mut self
—— 如果需要 &self
那么 &mut
将在 &
后面,并且 & &mut
的行为与 & &
相同。因为FnMut
调用必须采取&mut self
,所以需要声明borrows_mutably
mut
.
但在这种情况下我不需要将其声明为
,因为看起来我没有在我的代码中调用闭包,而是将它传递给另一个函数(mut
),该函数又将调用它。sort_by_key
可变性是绑定的属性,而不是值的属性。如果您采用不可变绑定并将其分配给可变绑定,则可以改变它。
例如编译失败:
let foo = vec![1];
foo.push(2);
但这确实编译!
let foo = vec![1];
let mut foo2 = foo;
foo2.push(2);
我们只是将
foo
中的值移动到一个不同的可变绑定中。现在我们可以改变它了。
sort_by_key
也发生了同样的事情,只是它被函数调用混淆了。当您将闭包传递给此函数时,您是在 放弃它。 sort_by_key
函数在可变绑定 中接收此参数,现在可以在其中调用它:
pub fn sort_by_key<K, F>(&mut self, mut f: F)
// ^^^
请注意,
f
的可变性 not 是函数签名的一部分,这就是它没有出现在文档中的原因。参数的可变性是函数私有的细节——函数是否计划改变它被赋予的任何值与调用者无关。
澄清一下,
FnMut
闭包值可以存储在不可变绑定中,但不能从那里调用。它需要被移动到一个可变绑定中才能被调用。 list.sort_by_key(func);
行是您代码中的作用,只是不是很明显。