gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-29 08:04:41 27 4
gpt4 key购买 nike

我有以下代码片段:

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/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com