rust - 所有权,关闭,FnOnce : much confusion

标签 rust closures move mutable borrowing

我有以下代码片段:

fn f<T: FnOnce() -> u32>(c: T) {
    println!("Hello {}", c());
}

fn main() {
    let mut x = 32;
    let g  = move || {
        x = 33;
        x
    };

    g(); // Error: cannot borrow as mutable. Doubt 1
    f(g); // Instead, this would work. Doubt 2
    println!("{}", x); // 32
}

疑问1

我什至无法运行我的闭包。

疑问2

... 但我可以根据需要多次调用该闭包,前提是我通过 f 调用它。有趣的是,如果我声明它 FnMut,我会得到与疑问 1 相同的错误。

疑问3

FnFnMutFnOnce 特征定义中,self 指的是什么?那是闭包本身吗?还是环境? 例如。来自文档:

pub trait FnMut<Args>: FnOnce<Args> {
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}

最佳答案

了解闭包的实际工作原理需要一些关于 Fn* trait 系列的基础知识。你有以下特点:

  • FnOnce ,顾名思义,它只能运行一次。如果我们查看文档页面,我们会发现特征定义与您在问题中指定的几乎相同。但最重要的是以下内容:“call”函数采用 self,这意味着它使用实现 FnOnce 的对象,因此就像任何采用 a 的特征函数一样self 作为参数,它获取对象的所有权。
  • FnMut ,它允许对捕获的变量进行突变,或者换句话说,它需要 &mut self。这意味着,当您进行 move 时 || {} 闭包,它会将您引用的任何在闭包范围之外的变量 move 到闭包的对象中。闭包的对象有一个不可命名的类型,这意味着它对每个闭包都是唯一的。这确实迫使用户采用闭包的某种可变版本,因此 &mut impl FnMut() -> ()mut x: impl FnMut() -> ()
  • Fn ,这通常被认为是最灵活的。这允许用户采用实现特征的对象的不可变版本。此特征的“调用”函数的函数签名是这三个函数中最容易理解的,因为它只需要对闭包的引用,这意味着您在传递或调用它时无需担心所有权。

解决您个人的疑问:

  • 疑问 1:如上所示,当您将某些内容 move 到闭包中时,该变量现在由闭包拥有。本质上,编译器生成的内容类似于以下伪代码:
struct g_Impl {
    x: usize
}
impl FnOnce() -> usize for g_Impl {
    fn call_once(mut self) -> usize {

    }
}
impl FnMut() -> usize for g_Impl {
    fn call_mut(&mut self) -> usize {
        //Here starts your actual code:
        self.x = 33;
        self.x
    }
}
//No impl Fn() -> usize.

默认情况下,它调用 FnMut() -> usize 实现。

  • 疑问 2:这里发生的是 closures are Copy只要他们捕获的每个变量都是Copy,意味着生成的闭包将被复制到f中,这样f就结束了获取它的 Copy。当您将 f 的定义更改为采用 FnMut 时,您会收到错误,因为您面临着与怀疑 1 类似的情况:您正在尝试调用一个函数当您将参数声明为 c: T 而不是 mut c: Tc: &mut 时,接收到 &mut self T,在 FnMut 的眼中,两者都符合 &mut self 的条件。
  • 最后,疑惑3,self参数是闭包本身,它已经捕获或 move 一些变量到自身,所以它现在拥有它们。

关于rust - 所有权,关闭,FnOnce : much confusion,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56743984/

相关文章:

c++ - std::move 为 "in-place operation"

c++ - 将 unique_ptr 持有的数组 move 到 vector 的数组存储中

rust - 是否可以在特征中使用构造函数?

rust - 是否可以为由所有实现特征的类型组成的任何元组自动实现特征?

grails - 我可以在 GSP 之外使用 grails 标签吗?

groovy - 如何访问 groovy 闭包注释?

kotlin - 如何解决 Rust 中的继承问题?

rust 宏 : Calling function dependent on expression

exception - Scala:无法捕获闭包内抛出的异常

c++ - 何时在函数调用中使用 move