试图理解 Rust 中的可变闭包

问题描述 投票:0回答:1

我是 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...
)。

rust closures
1个回答
1
投票

我在这里和那里阅读了很多帖子,我的理解是需要将闭包定义为

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);
行是您代码中的作用,只是不是很明显。

© www.soinside.com 2019 - 2024. All rights reserved.