为啥我可以在结构的类型参数中编写函数类型?

Posted

技术标签:

【中文标题】为啥我可以在结构的类型参数中编写函数类型?【英文标题】:Why am I able to write a function type in the type parameter of a struct?为什么我可以在结构的类型参数中编写函数类型? 【发布时间】:2021-12-25 06:40:42 【问题描述】:

如果我理解正确的话,在 Rust 中,每个闭包类型都有一个无法写出的唯一类型。我也认为这适用于函数,但是,我可以执行以下操作,其中我在 get_struct_1get_struct_2 的返回类型中显式编写类型参数:

struct FooStruct<F>
where F: Fn(i32) -> i32

    f: F,


fn foo(x: i32) -> i32 
    2*x


fn bar(x: i32) -> i32 
    -1*x


fn get_struct_1() -> FooStruct<fn(i32) -> i32>

    FooStruct  f: foo 


fn get_struct_2() -> FooStruct<fn(i32) -> i32>

    FooStruct  f: bar 


// This does not work - the trait has to be boxed
//fn get_struct_3() -> FooStruct<Fn(i32) -> i32>
//
//    FooStruct  f: |x| 10*x 
//

fn main() 
    let mut x = get_struct_1();
    // Why does this work - do bar and foo have the same type?
    x = get_struct_2();
    // Why does this work - doesn't a closure have its own unique, unwriteable type?
    x = FooStruct  f: |x| 10*x ;

    let mut y = FooStruct  f: |x| 10*x ;
    // Does not work - no two closures have the same type.
    //y = FooStruct  f: |x| 10*x ;
    // Does not work - even though the 'other way around' worked with x.
    // But _does_ work if I type-annotate y with FooStruct<fn(i32) -> i32>
    //y = get_struct_1();

我认为 Rust 在处理类型参数的方式上是单态的。所以如果我这样做

struct FooStruct 
    f: Box<dyn Fn(i32) -> i32>

程序会在运行时动态确定运行哪个f,但FooStruct&lt;F&gt;版本避免了动态调度。

这个例子似乎不同意这一点。如果x = get_struct_2(); 行位于if 语句中,编译器将无法确定x 是否持有函数foobar 的包装版本。

【问题讨论】:

【参考方案1】:

闭包(以及与此相关的函数)确实具有独特的、不可写的类型。但是,当它们不捕获任何变量时,它们也可以(并且也隐式地*)转换为函数指针,而您的却没有。这基本上是它起作用的原因:

fn main() 
    // closure is inferred to be a function pointer
    let mut f: fn() -> i32 = || 5;
    // assigning a different function pointer
    f = || 6;

但这不是:

fn main() 
    // closure is inferred to be a unique closure type
    let mut f = || 5;
    // uh oh! different closure type, errors
    f = || 6;

* 与其说是隐式转换,不如说是隐式类型推断

【讨论】:

好的,这很有帮助。但这是否意味着在您的第一个示例中,动态调度是在调用 f 时发生的? 并非如此。 f 存储一个指向函数的指针,当您调用 f 时,它指向的任何内容都会被调用。你可以说它是动态调度,f 指向的东西可以改变,但不涉及 vtables,就像处理dyn Trait 时一样

以上是关于为啥我可以在结构的类型参数中编写函数类型?的主要内容,如果未能解决你的问题,请参考以下文章

当一个函数无返回值时,函数的类型应定义为啥

为啥我的 Haskell 函数参数必须是 Bool 类型?

为啥我只能在类型参数位置传递部分应用的类型构造函数?

如果结构是值类型,为啥我可以新建它? [复制]

为啥我需要在这里指定模板化函数的模板参数类型?

Swift:为啥函数有参数和返回值类型?