While experimenting with closures I've come up with the following code:
fn m3(a: bool) -> impl Fn(i32) -> i32{
if (a){
|x| x+1
}
else {
|x| x
}
}
Considering that both of these closures are essentially different types I'm not really sure why does this work. After adding return keyword before the closures:
I think this is a case of weird type inference nonsense.
It only works in the first case because your closures don't capture any environment so they get coerced into function pointers.
When you explicitly return, the compiler takes the exact type of the returned value (the opaque closure type) and decides that's what the function returns, so the two different types can't be unified into function pointers.
When you use implicit return the compiler doesn't have a fixed type for the return type, so the inference machinery kicks in and coerces them to function pointers.
You can fix the return version by adding an explicit function pointer cast
In the case of both return and retval = there's an ordering of operations where the "first" operation gets to decide the type.
When you use the whole if as an expression both operations (returning each branch's closure) are happening "at the same time" from the perspective of the type checker since they're all part of the same expression.
I would generally expect _foo to be faster since it's a single closure type. It can easily be inlined that way. Boxing and returning function pointers both make optimizations harder.
The first return forces the type checker to choose fn(i32) -> i32 as the return type for the whole function. So the second return gets coerced because it can't choose the return type for the function anymore, but there is a valid coercion that would make the types compatible.
I'm not entirely sure what you mean, but once a value is cast to a function pointer you can't recover the information about where it came from, no.
If you mean "can I pass a function pointer to something expecting a Fn?" then the answer is yes. Function pointers implement the appropriate Fn traits.
So essentially when there is an implicit return Rust tries to coerce returned expressions into universal type of some sort because the type of the function is not fixed and when there is an explicit return Rust takes opaque type and sets it to return type of the function which is not compatible with other return types right?
I suspect it's actually the if expression that forces both branches to coerce to a common type (a fn pointer in this case), in a way that return + RPIT doesn't. To illustrate, this compiles (Rust Playground):
fn m3(a: bool) -> impl Fn(i32) -> i32 {
let retval = if a { |x| x + 1 } else { |x| x };
return retval;
}