How do I pass clonable Fn around and actually be able to construct and call it?

There are billions of posts on closures yet still I cannot understand how to pass a FnOnce around.

#[derive(Clone)]
pub struct Task
{
	pub(super) id: usize,
	pub(super) time: Instant,
	pub(super) callback: Box<dyn Cb>,
}
pub trait ClonableCb
{
	fn clone_box(&self) -> Box<dyn Cb>;
}
pub trait Cb: ClonableCb + Fn() + Send + Sync + 'static {}

impl<T> ClonableCb for T
where
	T: Cb + 'static + Clone,
{
	fn clone_box(&self) -> Box<dyn Cb>
	{
		Box::new(self.clone())
	}
}
impl Clone for Box<dyn Cb>
{
	fn clone(&self) -> Self
	{
		self.clone_box()
	}
}

This is my ClonableCb type and in theory it works fine but I cant construct one apparently neither
Box::new(move || {}) nor move ||{} is dyn Cb.

I don't understand why this is so difficult tho. If everything inside a closure is clonable what is the problem cloning the thing itself?

move || println!("test"); //gives error
//the trait `Cb` is not implemented for `[clo
//sure@src/timers/mod.rs:123:57: 125:4]

You never implement Cb for anything, and ClonableCb is only implemented for things that implement Cb.

1 Like

Cb is a sub trait of ClonableCb which is in tern Fn so doesnt it make Cb a Fn by default. I saw this pattern on StackOverflow earlier searching for making closures clonable (which is surprisingly difficult for some reason)

The syntax you are using tells the compiler that anything that implements Cb must also implement ClonableCb and Fn() and Send and Sync, but it does not say that anything that implements those traits must be Cb. Only an impl Cb for block can make types implement the trait.

4 Likes

Why don't you use Arc<dyn Fn() + Send + Sync> instead?

because I cannot apparently call and arced Fn

So basically I am back to square one I cannot implement Cb on T:Fn() because its not Clone

It is possible to call an arced Fn.

2 Likes

You can.

1 Like

last time I tried I got error saying I cannot move the closure out of the arc let me reproduce the error

it just worked My previous try used a FnOnce so apparently, that was the issue. Still cannot understand why its so difficult to make clonable closures tho. I just don't get it what possible problem it can cause to make closures clonable in an easy way

I guess you tried to call Arc<dyn FnOnce()> or similar at that time.

1 Like

exactly

is there a way to call a reference to a closure? if this is possible I don't even need an arc or clone

with fn instead of fnonce it just works

It's possible only with Fn, since FnMut must be called with non-shared reference and FnOnce must be consumed by the call.

1 Like

You can make a Fn out of a FnOnce + Clone rather easily though (pre-type-erasure).

    pub fn new_with_clonable<T>(callback: T) -> Self
    where
        T: FnOnce() + Clone + Send + Sync + 'static,
    {
        // n.b. `new_with` requires `Fn()`
        Self::new_with(move || (callback.clone())() )
    }

Playground.

(You don't want &Fn here since you'd need a &'static Fn.)

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.