我是Newby 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,因为它正在改变它的环境.我不知道这个直觉是对是错.

无论如何,在try 另一个闭包时,我发现了一个打破我理解的例子.这一条:

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呢?首先,注意,访问可变引用through a shared reference不允许可变访问.这就是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上,只是它被函数调用混淆了.将闭包传递给此函数时,您是giving it away..sort_by_key函数接收此参数in a mutable binding,现在可以在其中调用它:

pub fn sort_by_key<K, F>(&mut self, mut f: F)
//                                  ^^^

注意,f的可变性是函数签名的not部分,这就是为什么它没有出现在文档中的原因.参数的可变性是该函数的私有细节--该函数是否计划改变它所给出的任何值与调用者无关.

需要说明的是,FnMut的闭包值可以存储在不可变的绑定中,但不能从那里调用.需要将其移动到可变绑定中才能调用.第list.sort_by_key(func);行在您的代码中实现了这一点,只是不是很明显.

Rust相关问答推荐

如何从Rust记录WASM堆内存使用情况?

为什么我的梅森素数代码的指数越大,速度就越快?

使用铁 rust S还原对多个数组执行顺序kronecker积

如何点击()迭代器?

正则表达式中的重叠匹配?(铁 rust 正则式发动机)

对reqwest提供的这种嵌套JSON struct 进行反序列化

Tokio';s io::用Cursor拆分<;Vec<;u8>>;赢得';t get the full writted data

处理带有panic 的 Err 时,匹配臂具有不兼容的类型

`use` 和 `crate` 关键字在 Rust 项目中效果不佳

Rust 如何将链表推到前面?

Rust与_有何区别?

如何将这些测试放在一个单独的文件中?

内部值发生变化时 Rc 的行为

将 `&T` 转换为新类型 `&N`

`map` 调用在这里有什么用吗?

匹配结果时的简洁日志(log)记录

有没有办法在 Rust 中对 BigInt 进行正确的位移?

BigUint 二进制补码

基于名称是否存在的条件编译

如何阅读 HttpRequest 主体