rustclosurestypingmonomorphism

Why am I able to write a function type in the type parameter of a struct?


If I understand correctly, in Rust every closure type has a unique type that cannot be written out. I also thought this applied to functions, however, I'm able to do the following, in which I explicitly write the type parameter in the return types of get_struct_1 and get_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();
}

I thought Rust was monomorphic in the way it handles type parameters. So if I do this

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

the program will dynamically determine which f to run at runtime, but the FooStruct<F> version avoids the dynamic dispatch.

This example seems to disagree with that. If the x = get_struct_2(); line were inside an if statement, the compiler would not be able to determine whether x was holding a wrapped version of the function foo or bar.


Solution

  • Closures (and functions for that matter) do have unique, unwriteable types. However, they can also be cast (and implicitly* too) to function pointers when they don't capture any variables, which yours don't. It's essentially the reason why this works:

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

    But this doesn't:

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

    * it's not so much an implicit cast as implicit type inference