fn main() {
let a = String::from("123");
let c1 = move || drop(a); // FnOnce
c1();
c1(); // Err => So FnOnce can only be called once
let c2 = || {}; // Fn
foo(c2); // Ok => Fn satisfies FnOnce && Fn can be called N times
// Why c2 can be called N times (Fn) while it can be called **only** once (FnOnce)?
// Q: What on earth `FnOnce` means? Only once or at least once?
}
fn foo<F: FnOnce()>(f: F) {
f();
}
I used to think, c1: impl FnOnce() means c1 can only be called once; F: FnOnce() means F should be able to be called at least once. So I can work with them.
But when I teach others this way, they always feel strange. Now I think the difference in two places is not very graceful.
The only possible explain I can come up with is:
FnOnce()always means it can only be called once
c2 is impl Fn
when passed to foo, impl Fn is downgraded into impl FnOnce
Is this explain better or correct? And it's appreciate a lot if the guidance to understand the ABI or rustc code provided.
Edit: replace "coerce" to "downgrade", I misused them.
Edit: the second understanding is wrong, since if FnOnce means callable only once, it is not super set of Fn, so cannot downgrade.
Edit:
To draw a conclusion, it's still better to understand in capability and requirement sides: FnOnce means: maybe can be called multiple times, and guranteed able to be called once, and the caller can call it only once.
c2 can be called multiple times (within main()), since it implements Fn as you correctly asserted. The trait bound F: FnOnce only applies to f in foo() since it's the trait bound of that function's parameter. Try passing something that implements Fn to foo() and calling it (f) multiple times withinfoo() and see what happens.
FnOnce means that the function can be called at least once, which in turn means, that if you do not have any other information on an object that implements FnOnce you can only call it once, since it's unknown whether it can be called multiple times.
There's no coercion happening. FnOnce is a supertrait of Fn, and it's implemented by all callables (since all callables must be callable at least once; otherwise, they would not be a "callable," after all).
You are confusing two sides of the same story.
Trait bounds are necessarily two-faced:
on the one hand, they specify what the implementation can rely on
on the other hand, they enforce what the caller must satisfy.
#2 must necessarily be a superset of #1, otherwise the implementation would make no sense (how do you call Display::fmt on a type that doesn't implement Display, for example)? However, it does not have to be the same. Your code won't be prevented from passing a Display + Clone type to a generic function that expects Display only; that would make zero sense and would render capability-based generics utterly useless in practice.
If you consider these completely obvious facts in the context of what FnOnce and Fn are, you'll get a logical explanation:
FnOnce as a requirement means "callable at least once", so when passing a callable to a higher-order function accepting FnOnce, it will accept things that are callable either exactly once or more than once.
FnOnce as a capability means "callable at most once", so when you are actually trying to use it in a generic context, you can only call it once, because generics are purposefully not transparent (as they would be in e.g. C++). Something that says to the outside world "I'll work with an FnOnce" must in fact work with anyFnOnce, including those that are actually one-shot.
Your implementation can't assume more capabilities (e.g. Fn) than declared, because, again, it wouldn't then make sense when instantiated with an actual type that only implements as much as promised, and not more.
FnOnce has a method that takes self: you can only call it once, after that it consumes the closure and you can no longer call it again (unless it implements Copy);
FnMut has a method that takes &mut self: you need mutable access to the closure in order to call it. It also implements FnOnce because if you have an owned self then you can call any method taking references, including the one for FnMut;
Fn has a method that takes &self: you only need a shared reference to call it. It also implements FnMut because from its method you can convert the &mut self to a shared reference and call Fn's method. And the same argument for implementing FnOnce for FnMut also extends here.
Maybe I didn't understand the world coerce correctly. 42 as usize is coercion, but when passing &'static str to foo(&'a str), coercion also happens...right?... I should use "downgrade"...
Oh, will impl Fn downgrade to impl FnOnce, absolutely it's why all works.
If you can understand why the playground compiles -- why c2 can be copied in main in this playground even though it was passed to a function with a C: Clone constraint -- you should be able to understand why c2: impl Fn() in your OP can be called multiple times in main even though it was passed to a function with a F: FnOnce constraint.
FnOnce is "I can call this, at least once" Fn is "I can call this, several times".
This could be renamed as:
FnOnce -> Fn
Fn -> FnMulti
(Why it is name this way is not the point. It is what it is).
So Fn is a supertrait of FnOnce. It provides more "features" than FnOnce.
A Fn object is also a FnOnce as it can be called once.
c1 is FnOnce but not Fn. So you can call it, but not several times => You can call it only once. c2 is FnOnce and Fn. So you can call it several times.
If foo, F: FnOnce() means that the only thing you will know about f in foo implementation is FnOnce and so you can call it only once (you don't have Fn, so you can't call it several times).
You are limiting yourself in the implementation, and so you declare to the caller that you will be able to call only once f. Caller is free to give you a Fn or a FnOnce function (as Fn is a supertrait of FnOnce, a function implementing Fn must also implement FnOnce)
To be exact, I'm confusing supertrait and superset (which is the term used in your link)
But you are right, FnOnce is a supertrait of FnMut which is a supertrait of Fn.
And Fn (call mutiple time without mutating states) is a superset of FnMut (call multiple time, potentially mutating states) which is a superset of FnOnce (call, at least once)
BTW, I realize I have simply paraphrased the FnOncedocumentation
The root of the confusion is that many of us didn’t know trait precisely.
All traits, including FnOnce, are supposed to be seen on two sides.
When requiring, like F: FnOnce, FnOnce means the callable.
When consuming, like f(), FnOnce means callable only once.
More generically, a function needs T: Animal, then T is at least an animal, so Dog Cat can be passed in. However, the user got a t, which satisfies Animal, the user can only treat it as an animal, not dog or cat, or everything will be in a mess.
So, FnOnce, is callable, no constrain on how many times. When requiring, any callable is compatible. When consuming, none knows how many times exactly it can be called, so we should call it only once.