A FnMut
closure cannot be cloned, for obvious reasons, but a Fn
closure has an immutable scope; is there some way to create a "duplicate" of a Fn
closure?
Trying to clone it results in:
error[E0599]: no method named `clone` found for type `std::boxed::Box<std::ops::Fn(i8, i8) -> i8 + std::marker::Send + 'static>` in the current scope
--> src/main.rs:22:25
|
22 | fp: self.fp.clone(),
| ^^^^^
|
= note: self.fp is a function, perhaps you wish to call it
= note: the method `clone` exists but the following trait bounds were not satisfied:
`std::boxed::Box<std::ops::Fn(i8, i8) -> i8 + std::marker::Send> : std::clone::Clone`
Is it safe to somehow pass a raw pointer to a Fn
around, like:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
Technically, the above works, but it seems quite weird.
Here's an example of what I'm trying to do:
use std::thread;
struct WithCall {
fp: Box<Fn(i8, i8) -> i8 + Send>,
}
impl WithCall {
pub fn new(fp: Box<Fn(i8, i8) -> i8 + Send>) -> WithCall {
WithCall { fp: fp }
}
pub fn run(&self, a: i8, b: i8) -> i8 {
(self.fp)(a, b)
}
}
impl Clone for WithCall {
fn clone(&self) -> WithCall {
WithCall {
fp: self.fp.clone(),
}
}
}
fn main() {
let adder = WithCall::new(Box::new(|a, b| a + b));
println!("{}", adder.run(1, 2));
let add_a = adder.clone();
let add_b = adder.clone();
let a = thread::spawn(move || {
println!("In remote thread: {}", add_a.run(10, 10));
});
let b = thread::spawn(move || {
println!("In remote thread: {}", add_b.run(10, 10));
});
a.join().expect("Thread A panicked");
b.join().expect("Thread B panicked");
}
I have a struct with a boxed closure in it, and I need to pass that struct to a number of threads. I can't, but I also can't clone it, because you can't clone a Box<Fn<>>
and you can't clone a &Fn<...>
.
Here is the working code in 1.22.1
The intent is to make this work.
The original code as suggested at the top was used.
I started with cloning a
Fn
closure.Ended up adding
Arc
aroundFp
in the structWithCall
Play rust : working code Gist : working code in 1.22.1
What you are trying to do is call a closure from multiple threads. That is, share the closure across multiple threads. As soon as the phrase "share across multiple threads" crosses my mind, my first thought is to reach for
Arc
(at least until RFC 458 is implemented in some form, when&
will become usable across threads).This allows for safe shared memory (it implements
Clone
without requiring its internal type to beClone
, sinceClone
just creates a new pointer to the same memory), and so you can have a singleFn
object that gets used in multiple threads, no need to duplicate it.In summary, put your
WithCall
in anArc
and clone that.playground
Old answer (this is still relevant): It is quite unusual to have a
&mut Fn
trait object, sinceFn::call
takes&self
. Themut
is not necessary, and I think it adds literally zero extra functionality. Having a&mut Box<Fn()>
does add some functionality, but it is also unusual.If you change to a
&
pointer instead of an&mut
things will work more naturally (with both&Fn
and&Box<Fn>
). Without seeing the actual code you're using, it's extremely hard to tell exactly what you're doing, but(This is partly due to
&T
beingCopy
and also partly due to reborrowing; it works with&mut
as well.)Alternatively, you can close-over the closure, which likely works in more situations:
There's no inherent reason a
FnMut
can't be cloned, it's just a struct with some fields (and a method that takes&mut self
, rather than&self
orself
as forFn
andFnOnce
respectively). If you create a struct and implementFnMut
manually, you can still implementClone
for it.Technically it works if you're careful to ensure the aliasing and lifetime requirements of Rust are satisfied... but by opting in to unsafe pointers you're putting that burden on yourself, not letting the compiler help you. It is relatively rare that the correct response to a compiler error is to use
unsafe
code, rather than delving in to the error and tweaking the code to make it make more sense (to the compiler, which often results in it making more sense to humans).Rust 1.26
Closures implement both
Copy
andClone
if all of the captured variables do. You can rewrite your code to use generics instead of a boxed trait object to be able to clone it:Before Rust 1.26
Remember that closures capture their environment, so they have a lifetime of their own, based on the environment. However, you can take references to the
Fn*
and pass those around further, or store them in a struct:I would say that it is not universally safe to convert the
Fn*
to a raw pointer and pass it around, due to the lifetime concerns.