首页 > 解决方案 > 为什么在 Rust 的泛型中接受 `fn(..)` 而不是 `Fn(...)`?

问题描述

为什么在 Rust 中接受fn(..)而不是泛型?Fn(...)

我的直觉是

所以:

下面是一些伪代码以获得灵感,请随时对其进行润色,这不仅限于这些情况,因为使用Vec<dyn fn(X)->Y>vs Vec<Fn(X)->Y>(或使用unboxed_closures语法;)可能更好地演示内存大小差异。

草图,示例,但不限于:

// peusdo code
pub struct Foo <F,X,Y>
where
F: fn(X)->Y ,
...
{
    fx: F,
}

impl Foo<F,X,Y> where
F: fn(X)->Y,
...
{
    pub fn new(foo: F) -> Self;
    ...
}

// peusdo code
pub struct FooByRef <'a,X,Y>
where
F: &'a dyn fn(X)->Y ,
...
{
    fx: F,
}

impl Foo<'a,X,Y> where
F: &'a dyn fn(X)->Y,
...
{
    pub fn new(foo: &'a dyn fn(X)->Y) -> Self;
    ...
}

标签: assemblyoptimizationrustclosures

解决方案


Here are some examples of how fn() and Fn() can be used in structs.

struct Foo<F>
// there is no need for trait bounds in a struct definition
{
  function: F,
}

// the impementation is generic over a generic type `F`, hence `impl<F> Foo<F>`
// `impl Foo<F>` would be an implementation for a concrete type called `F`
impl<F> Foo<F>
where F: Fn()
{
  fn new(function: F) -> Self {
    Self { function } // equivalent to `Foo { function: function }`
  }
  fn call_function(&self) {
    (self.function)();
  }
}

Foo requires a specific type which implements Fn() in order to be used. This makes Foo restricted to the single specific type, be it fn() or the type of one concrete closure or function. The restriction, however, makes it possible to store only the context of F since the implementation can be inferred from the type.

struct Bar {
  function: fn(),
}

impl Bar {
  fn new(function: fn()) -> Self {
    Self { function }
  }
  fn call_function(&self) {
    (self.function)()
  }
}

Bar is equivalent to Foo<fn()>, the only thing stored in it is the function pointer. It is restricted to functions and closures with no context, which can be cast to fn().

struct DynRefFoo<'a> {
  function: &'a dyn Fn(),
}

impl<'a> DynRefFoo<'a> {
  fn new(function: &'a dyn Fn()) -> Self {
    Self { function }
  }
  fn call_function(&self) {
    (self.function)();
  }
}

DynRefFoo may contain references to multiple types across its instances. For this reason, pointers to both the context and the implementation of the Fn() trait have to be stored in the struct.

In terms of the Fn trait, the context of fn is the pointer and the implementation simply calls the function behind the pointer.

Additionally, each function has its own zero-sized type which implements Fn so there is a slight difference between using a concrete function and a pointer to that function as dyn Fn.

To sum up:

  • dyn Fn works for everything.
  • fn can store functions and no-context closures only.
  • Fn bounded generic types are limited to a single closure since each closure has its own type.

推荐阅读