pub fn call_twice<X, Y, F>(f: F, x1: X, x2: X) -> (Y, Y)
where
F: FnOnce(X) -> Y + Copy,
{
(f(x1), f(x2))
}
No, because there are non-Copy
closures that satisfy Fn
:
fn main() {
use std::sync::Arc;
let a = Arc::new(5);
// dbg!(call_twice_copy(move |x| x*(*a), 2, 3)); // compile err
dbg!(call_twice_fn(move |x| x*(*a), 2, 3));
}
Fn
can capture Cells. A Copy
+ FnOnce
type would need such capture to be in Rc for its closure to maintain state.
Also Copy
is very strict so not all Fn*
captures can be Copy.
This makes a much more compelling example than my original one, I think:
fn main() {
use std::cell::Cell;
let a = Cell::new(0);
let accumulate = move |x| {
let val = x + a.get();
a.set(val);
val
};
//dbg!(call_twice_copy(accumulate, 2, 3)); // compile err
dbg!(call_twice_fn(accumulate, 2, 3));
}
If I replace Copy
with Clone
, it will work again.
pub fn call_twice_clone<X, Y, F>(f: F, x1: X, x2: X) -> (Y, Y)
where
F: FnOnce(X) -> Y + Clone,
{
(f.clone()(x1), f(x2))
}
I wonder if Clone
is what Fn
does under the hook?
No, it does not clone.
To understand how it works, consider this example:
fn main() {
let val = "foo".to_string();
let my_fn = move || {
println!("{}", val);
};
my_fn();
my_fn();
}
That's equivalent to this:
#[derive(Clone)]
struct MyClosure {
val: String,
}
impl MyClosure {
fn call(&self) {
println!("{}", self.val);
}
}
fn main() {
let val = "foo".to_string();
let my_fn = MyClosure {
val,
};
my_fn.call();
my_fn.call();
}
On the other hand, this:
fn main() {
let val = "foo".to_string();
let my_fn = move || {
println!("{}", val);
};
(my_fn.clone())();
my_fn();
}
is equivalent to this:
#[derive(Clone)]
struct MyClosure {
val: String,
}
impl MyClosure {
fn call(&self) {
println!("{}", self.val);
}
}
fn main() {
let val = "foo".to_string();
let my_fn = MyClosure {
val,
};
my_fn.clone().call();
my_fn.call();
}
So one of them will clone the String
, and the other one wont.
It will often compile, but it may produce different results due to the extra clone that @alice points out. For example, the accumulator I posted above:
fn main() {
use std::cell::Cell;
let a = Cell::new(0);
let accumulate = move |x| {
let val = x + a.get();
a.set(val);
val
};
assert_eq!((2,5), call_twice_fn(accumulate.clone(), 2, 3));
assert_eq!((2,5), call_twice_clone(accumulate.clone(), 2, 3)); // Fails
}