https://github.com/rust-lang/rust/issues/44490 this issue says that rust will copy or clone a closure when need, why in my code the clone method doesn't work? How to clone a boxed closure or any other way to fix my code?
I'm not sure it can be fixed with boxed Fn but it should work with function pointer.
You just have to replace your dyn Fn with fn and since they all have the same size you can remove the Box. They are Copy too so no need for clone.
This will only work if you don't capture the environment however.
As a side note, return is usually reserved for early returns, it's more idiomatic to just have the value without the return keyword.
Your bound issue (the compiler error) was because your closure is not considered Clone anymore once you put it into its Box.
The issue is not that your closure is Clone or not, it to keep that information until the call to clone.
We would like something like Box<dyn Fn(i64, i64) -> i64 + Clone> but it's not possible. If you really want to you can but it's a lot easier with function pointer.
Closures have a known size, you can do:
let add = |x: i64, y: i64| x + y;
But you can't name the type, each closure has it's own, something like [closure@src/main.rs:5:19: 5:41].
To store it however we need a name, we can use function pointers or trait objects.
If we go with trait objects, we loose the Sized bound and need some kind of pointer.
Fn-like trait is not equivalent to closures and function pointers to functions.
You can make a Fn-like trait object from a function and a function pointer from a closure (that doesn't capture anything, this is the restriction).
But when I changed to add1: Testfn, it compiles, and if I remove this type declaration for add1, it compiles. I think when using a closure or function as a parameter, rust will first treat as a function pointer
Also this example shows boxed closure can't be cloned, but boxed function pointer can.
By the way, if you choose to use function pointers rather than a trait object dyn FnClone encapsulating both the callable interface (Fn(...) -> _) and the cloneable interface (clone_box), you no longer need to use Box:
impl P {
pub
fn new () -> Self
{
let mut fm = HashMap::<&'static str, fn(i64, i64) -> i64>::new();
fm.insert("add1", add1);
fm.insert("add2", add2);
Self { fm }
}
You can also replace Box<dyn Fn()> with Arc<dyn Fn()>, which makes it clone-able. Technically it doesn't clone the closure itself, but can make clones of shared references to the same closure.
This is definitely the best solution in practice, since a dyn Fn is something that can be called / held concurrently, and that requires heap-allocation to be held in an owned fashion; hence negating the only "drawbacks" of Arc / Rc (no DerefMut and requires heap allocation).
On the other hand, if you are not using Fn(...) -> _ + Send + Sync + 'static, but just Fn(...) -> _ + 'static (as seems to be your case), you can go and use Rc instead of Arc, since the latter would provide no extra functionality:
use ::std::rc::Rc;
let add1: Rc<dyn Fn(i32, i32) -> i32> = Rc::new(|a, b| a + b);
dbg!(add1.clone()(1, 2));
Digging deeper I have a much generic question, in meta-programming field, we often need to generate a function from a factory method and then pass to a template method, like the code below:
fn main() {
let p1 = P1::default();
let f1 = generate_f(p1);
let res1 = call_f(f1.clone(), 2, 2);
println!("res1={}", res1);
}
fn call_f(f: Fn(usize, usize)->bool, a: usize, b: usize) -> bool {
f(a, b)
}
fn generate_f(t: impl T + 'static) -> Box<dyn Fn(usize, usize) -> bool> {
let cb = move |a: usize, b: usize| -> bool {
t.add1(a, b) == 5
};
return Box::new(cb);
}
pub trait T {
fn add1(&self, a: usize, b: usize) -> usize;
}
#[derive(Default)]
pub struct P1 {}
impl T for P1 {
fn add1(&self, a: usize, b: usize) -> usize {
a + b + 1
}
}
In this code snippet, the method generate_f is a factory function that will generate a lot of functions, based on the incoming t. And then the generated function is passed to a template function call_f. but this call_f function will only receive a closure but the generated function is a boxed closure, we know a boxed closure can't be cloned, so this code doesn't compile.
A good way is modify the call_f makes it accept a boxed closure, but usually we will just define it accepting a closure, so what should I do in this occasion
In the same way that you needed : impl T, you "need" : impl Fn(...) -> _ : Fn is yet another trait, it's just that the Fn trait has sugar support from Rust (the () operator).
But all we are doing above we could do it with your trait T.
Thanks, a Rc wrapped closure is actually better than a Boxed closure, because Rc support clone while box not, and a rc closure can be derefered while boxed closure not, is that right?
You can clone a Box, you just have to help the compiler understand what you are doing (link in a previous reply).
You can also deref a Box but you don't have to since Box<dyn Fn> implements Fn itself.
Rc / Arc do not require the environment captured by the closure to be cloneable, whereas Box does.
Your callbacks captured no environment, which were thus trivially Cloneable, but because of the type erasure of dyn ... (trait objects), any functionality not described by one of the traits within the trait object is lost.
You'd need something like Box<dyn Clone + Fn(...) -> _>, but when thinking about it this cannot work for technical reasons (Clone::clone(&self) -> Self is not object-safe (it requires static knowledge of the size of Self, also lost by trait object erasure).
So we need something like Clone but with an object-safe interface; that's what @leudzFnClone trait was for, and with that trait Box<dyn FnClone> can indeed be Cloned (which is, by the way, only marginally cheaper than Rc<dyn Fn(...) -> _> cloning when no environment is captured, and in that case raw function pointers did the job).
For an example of a closure capturing environment, take:
fn add_n (n: i32) -> impl Fn(i32, i32) + Clone + 'static
{
move // this closure captures `n: i32`; a function pointer no longer suffices
|x: i32, y: i32| -> i32 {
x + y + n
}
}
Cloneable callable: recap
That's why, if you know you won't be capturing environments, fn(...) -> _ function pointers suffice
fn(...) -> _
but for everything else, if cloning the closure is required (by the way, this does not come as often as a requirement as you may think, since a borrow of a closure is also a closure), you should use:
Rc<dyn Fn(...) -> _ + 'static> in a single-threaded context,
Now I have a better understanding about this piece of code. By default a boxed closure can not be cloned, so you created a new trait FnClone, it's also a closure, then implement clone trait for Box<dyn FnClone> so the boxed closure can be cloned, it's a very clever, I can never come up with this solution.