assembly - 为什么在 Rust 的泛型中接受 `fn(..)` 而不是 `Fn(...)`?
问题描述
为什么在 Rust 中接受fn(..)
而不是泛型?Fn(...)
我的直觉是
- 一方面,如果代码接受带有 context -> 的闭包
Fn()
,但我们提供“无上下文”闭包fn(...)
,那么编译器应该针对fn(...)
. - 另一方面,也许在实际内存和程序集表示方面有优势
fn(...)
(例如,由于不需要存储上下文,占用更少的空间),这将使我们更愿意将我们的泛型类型限制为接受fn(...)
类型而不是 `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;
...
}
解决方案
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.
推荐阅读
- swift - 如何将 Mac 上的 SwiftUI 菜单命令连接到模型
- javascript - 如何为chartjs散点图中的每个数据添加颜色
- python - 将 int 和 string 从字符串 python 拆分为元组
- openstack - Radosgateway 服务器(客户端)到 Haproxy(然后是后端 keystone 容器)为什么获取客户端请求所需的时间很高,例如 5 秒到 20 秒
- nginx - 当发出 GET 以外的请求时,如何清除或重置 nginx 缓存?
- sql - 简单的 SQL 任务
- javascript - firebase.auth().SignInWithPhoneNumber 不是函数
- strapi - 从 API(strapi)访问图像 URL - 这可能吗?
- python - 当我尝试使用变量列出数组时出现 TypeError
- excel - Excel 到 XML:日期到数字?