Compiling playground v0.0.1 (/playground)
error[E0277]: expected a `Fn<()>` closure, found `[closure@src/main.rs:28:24: 28:26]`
--> src/main.rs:29:9
|
29 | foo.call();
| ^^^^ expected an `Fn<()>` closure, found `[closure@src/main.rs:28:24: 28:26]`
|
= help: the trait `Fn<()>` is not implemented for closure `[closure@src/main.rs:28:24: 28:26]`
= note: wrap the `[closure@src/main.rs:28:24: 28:26]` in a closure with no arguments: `|| { /* code */ }`
= note: `[closure@src/main.rs:28:24: 28:26]` implements `FnMut`, but it must implement `Fn`, which is more general
note: required by a bound in `Foo::<R>::call`
--> src/main.rs:21:12
|
19 | fn call(&self)
| ---- required by a bound in this associated function
20 | where
21 | R: Fn() -> (),
| ^^^^^^^^^^ required by this bound in `Foo::<R>::call`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to previous error
I want to add that bound to avoid some type inference issues with arguments, but I don't see a reason to make new demand an Fn (even FnOnce would be sufficient there).
If I replace FnMut in the bound of the new method with Fn, then it does compile (Playground). It will also compile if I uncomment the foo.call() (Playground).
Your code works with the following change in main:
fn main() {
- let foo = Foo::new(|| println!("called"));
+ let closure = || println!("called");
+ let foo = Foo::new(closure);
foo.call();
}
The bound on Foo::new is FnMut so the closure is coerced to and treated as an FnMut. Once it's "stored" as an FnMut, all other type information about the closure is lost (I am speaking imprecisely because this is just how I understand it, not a precise technical explanation).
So when you try to call Foo::call, rust sees that the stored closure implements FnMut, but it doesn't implement Fn, which is the stricter bound. Even though the closure technically does implement Fn, rust doesn't know that because all it knows is that the closure is a FnMut, nothing more.
When you separate out the closure creation from the call, you create the closure in a place where it isn't automatically coerced into an FnMut.
Note that FnMut is a trait, and that F: FnMut() -> R is a bound, not a type! I don't see a reason why that bound should infer a type that fulfills less traits.
As far as I'm aware, Fn*-trait bounds from type inference influence closure type a lot. The presence of a, FnMut killing the Fn implementation is just one example.
Another example is how it's entirely impossible to get proper dependencies between lifetimes in arguments and return types, without the presence of such a trait bound. E. g. try to write a for<'a> Fn(&'a u8) -> &'a u8without that trait bound being visible for type inference at the place the closure is defined; IIRC that won't work.
Looks like the issue you linked lists a reason in the discussion here: the standard inference of whether a closure is also FnMut or only FnOnce will infer "also FnMut" for certain closures where an FnMut impl will subsequently run into borrow check errors, whilst an FnOnce-only implementation wouldn't.
I wonder whether similar examples exist for Fn vs only-FnMut.
I suppose this is not a water tight argument against changing the status quo, but it makes the situation harder to improve, especially if the goal is (as it always should be) that borrow checking should not influence program behavior (so deciding whether a trait is implemented based on whether borrow checking some code succeeds is probably a bad idea). In principle, I can imaging the compiler being/becoming smarter at discovering (and thus considering) this indirect Fn requirement in your code (within the main function body) though.
In order to infer FnMut vs. FnOnce we would have to know if the closure returns a reference into itself. I am not very experienced with the design of the compiler, but from what I gather, this information is not available until borrowck, which itself relies on type checking to be done first. This might be difficult to solve.
I've edited my previous comment and added the relevant argument: if the determination that FnMut is impossible is determined by "trying borrowck", that'd go a bit against current principles of borrow-checking not influencing types and behavior and the like.
If the compiler can argue based on the usage of the closure within main that FnMut is unnecessary (regardless of whether it would be possible) that does (to me, right now) seem like a better approach to improve the situation.
It's (sort of) intentional for now, because these "downgrades" are sometimes needed (as of yet).
I'll have to think about what I do. Maybe remove the bound (and require type annotations for the closure's arguments), or just put an Fn bound, even though not necessary.
Thanks for all the input. I learned a lot about closures again.
Oh, sorry, it was just speculation that it’s related to rustc not assuming some trait bounds that are always true, leading to having to write redundant bounds, but this is not that after all. (Rust of course does normally add implicit supertrait bounds but I thought this might be a corner case.)