I wrote some code to test FnOnce capturing, it seems the code below does not follow the rule.
Do I misunderstand it?
struct xx<T: FnOnce() -> *mut i64> {
f: T
}
if let Some(T) = v1_iter.next() {
let f1 = xx {
f: || -> *mut i64 {*T = *T + 20; T}
};
(f1.f)();
println!("{:?}", T); // it is OK to print the moved T
(f1.f)(); // BUT the f1.f is moved, which is not captured
}
Not exactly. A FnOnce is known to be callable once, in the sense of at least once. If you don't know anything else about the callable, then this least / lower bound becomes also an upper bound, as with many abstractions, so it effectively becomes an at most once.
In the OP example however, the xx struct does contain a fully visible F field, so when F = some Fn closure : Fn() : FnOnce(), the FnOnce bound is met but the additional information can allow multiple calls.
To see this, consider the following simpler example:
fn fn_once_identity<T> (x: T) -> T
where
T : FnOnce(),
{
x
}
let f = fn_once_identity(|| {
println!("Hello, World!");
});
f();
f();
despite the FnOnce bound, the returned value is still identical to the input value, so it keeps all its properties, such as it being Fn() on top of FnOnce.
So an FnOnce bound is only limiting when that is all the information we have about some unspecified type (e.g., a generic type or an impl FnOnce() type):
fn call_twice<F : FnOnce()> (f: F)
{
f();
f(); // ~ERROR: use of moved value
}
fn fn_once_eraser (f: impl FnOnce()) -> impl FnOnce() { f }
let f = fn_once_eraser(|| {});
f();
f(); // ~ERROR: use of moved value
Nice insight with the fn_once_identity! But yeah, since OP is calling it "through" type xx, the only information about the function is that it implements FnOnce.
Edit: ah no, now I'm confused. Why is the type of f1.f not deduced to be something that actually implements more than FnOnce? Shouldn't it be analogous to the fn_once_identity situation? Playground to follow along: Rust Playground
Ok, after some testing, I think it has to do with the FnOnce bound interacting with type inference and the compiler-generated closure type: Rust Playground