Reputation: 8174
In this code, I have the skeleton of an observable system. The documentation on implementing Observable and other decoupling patterns are usually missing a final step on allowing access to the listener while also having it be &mut
during the notification call, but that's what RefCell
is intended to handle. So I have this code all worked out, but I'm having a last piece of trouble getting my concrete T
into the &dyn Trait
.
use std::{cell::RefCell, rc::Rc};
pub trait Observer {
fn notify(&mut self);
}
#[derive(Default)]
pub struct Subject<'s> {
listeners: Vec<Rc<RefCell<Box<dyn Observer + 's>>>>,
}
pub fn wrap<T>(t: T) -> Rc<RefCell<Box<T>>> {
Rc::new(RefCell::new(Box::new(t)))
}
impl<'s> Subject<'s> {
pub fn add_observer(&mut self, observer: Rc<RefCell<Box<dyn Observer + 's>>>) {
self.listeners.push(observer)
}
pub fn notify(&mut self) {
for listener in &mut self.listeners {
listener.borrow_mut().notify();
}
}
}
#[cfg(test)]
mod test {
use super::{wrap, Observer, Subject};
#[derive(Default)]
pub struct Counter {
count: usize,
}
impl Observer for Counter {
fn notify(&mut self) {
self.count += 1;
}
}
#[test]
fn it_observes() {
let mut subject = Subject::default();
let counter = wrap(Counter::default());
subject.add_observer(counter); // mismatched types
for i in 1..5 {
subject.notify();
subject.notify();
assert_eq!(counter.borrow().count, i * 2);
}
}
}
The full error is
error[E0308]: mismatched types
--> src/observer.rs:48:30
|
48 | subject.add_observer(counter);
| ^^^^^^^ expected trait object `dyn Observer`, found struct `Counter`
|
= note: expected struct `Rc<RefCell<Box<(dyn Observer + 'static)>>>`
found struct `Rc<RefCell<Box<Counter>>>
I've seen (what looks like) this pattern used in a number of contexts, but I can't tell what either I'm missing or doing differently.
How do I get the T: Trait
out of its static dispatch and into dynamic dispatch?
Upvotes: 1
Views: 186
Reputation: 42716
You can take to boxing outside of your wrap
function, and box the counter itself with the proper box type:
pub fn wrap<T>(t: T) -> Rc<RefCell<T>> {
Rc::new(RefCell::new(t))
}
...
let counter: Box<dyn Observer> = Box::new(Counter::default());
let counter = wrap(counter);
Btw, once you have the dynamic dispatch, you have an dyn Observer
so you lose access to the Counter
itself. You will have to take ownership of it and downcast
the pointer to the concrete type again.
Upvotes: 2