为什么不直接使用 Fn、FnMut 或 FnOnce 作为类型?

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

Rust 中的闭包根据其捕获行为已经属于三个明确定义的类别之一(Fn、FnMut 和 FnOnce)。

如果只有这三种可能的类型,为什么还要麻烦 impl 呢?为什么不直接使用它们作为类型而不是 impl Trait?

rust rust-cargo
1个回答
0
投票

如果只有这三种可能

这是不正确的。

Fn
FnMut
FnOnce
不是类型,它们是特质。事实上,它们不可能只是类型。

虽然您定义的任何普通

fn

 都可以表示为一个简单的指针,但闭包是完全不同的事情。他们可以捕获他们的环境,这实际上意味着他们可以使用他们附近定义的变量。这也意味着他们需要将这些变量存储在某个地方,因为你无法真正知道闭包何时被执行。每个闭包可能想要捕获不同类型的变量,所有变量都有不同的大小,因此不可能有一种通用的类型可以包含您可能使用的任何类型的“上下文”。因此,通过编译器的魔法,创建了一个不可命名的类型,您可以使用简单的代码片段来确认:

let mut x = 5; let f = || { x += 1; }; println!("size_of({}) = {}", std::any::type_name_of_val(&f), std::mem::size_of_val(&f));

此打印

size_of(playground::main::{{closure}}) = 8
在幕后,上面代码中发生的事情与此接近:

let mut x = 5; struct Closure<'a> { // closure context x: &'a mut i32, } impl FnOnce<()> for Closure<'_> { type Output = (); extern "rust-call" fn call_once(self, _args: ()) -> Self::Output { // closure body *self.x += 1; } } let f = Closure { x: &mut x }; // now it can be called: f();
本例中的闭包对象必须包含对其修改的变量的可变引用,因此闭包类型的长度为 8 个字节(在 64 位平台上)。您可能想尝试一下不同的闭包捕获不同的东西,并注意到闭包类型的大小直接取决于它捕获的变量。


另请参阅:

Rust 闭包如何工作以及它如何执行闭包?

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