Let's say I want to build a simple callback system:
pub struct PropertyCallback<T> {
object: Weak<RefCell<T>>,
callback: Box<dyn FnMut(&mut T, &EventData)>,
}
impl<T> PropertyCallback<T> {
fn call(&mut self, event: &EventData) {
(*self.callback)(
&mut *self.object.upgrade()
.expect("Callback object has been dropped")
.borrow_mut(),
event,
);
}
}
The listener is wrapped in a RefCell
, we store a weak reference to it and the callback takes the event and may modify itself [the listener].
Now, let's have a struct that can store a callback in order to notify it if something interesting happens:
pub struct EventGenerator {
callback: PropertyCallback<???>
}
That's where the trouble starts. I don't want EventGenerator
to be generic, as any RefCell<T>
could be used as callback, but I still the generics inside PropertyCallback
.
Non-solution
I've tried using trait objects:
pub struct EventGenerator {
callback: PropertyCallback<Box<dyn Any>>
}
This does not work because it means that the only thing I can call back is a RefCell<Box<Any>>
. It also means that the &mut T
inside the calback closure becomes a Box<Any>
forcing a downgrade
in every callback implementation.
Ugly solution
I can take the call
method and put it into a trait:
pub trait PropertyCallbackTrait {
fn call(&mut self, old_value: &PropertyValue, new_value: &PropertyValue);
}
this allows me to use callback: Box<dyn PropertyCallbackTrait>
in my struct. While this does the trick, I really dislike the extra boilerplate for defining a separate trait for each of my structs where I want to erase generics.
What I want
Ideally, I'd like to have some type like Box<dyn PropertyCallback<_>>
that acts mostly like a trait object, but without the requirement of an extra trait.
I'll happily take alternative design suggestions for my callback system that don't suffer from this problem.