【问题标题】: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 的包装版本。

【问题讨论】:

    标签: rust closures typing monomorphism


    【解决方案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 时一样
    猜你喜欢
    • 1970-01-01
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    • 1970-01-01
    • 1970-01-01
    • 2016-06-05
    • 1970-01-01
    相关资源
    最近更新 更多