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
.
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