This problem was already mentioned (How can I correctly implement observer pattern in Rust?), however, I didn’t find any appropriate solution.
Here is the basic code structure I wrote to implement observable pattern. I omitted rest of the methods for simplicity and compactness.
pub struct Observable<'l, T> {
value: T,
observers: Vec<Box<FnMut(&T) + 'l>>,
}
impl <'l, T> Observable<'l, T> {
pub fn subscribe<Callback>(&mut self, observer: Callback) where Callback: FnMut(&T) + 'l {
self.observers.push(Box::new(observer));
}
fn notify_observers(&mut self) {
for observer in &mut self.observers {
observer(&self.value);
}
}
}
However, implementation above is broken by design. And here is why:
This is what I’m trying to achieve in terms of lifetime:
lifetime of observable
|--------------------------------------------|
|-----------------------|
lifetime of observer
Since I store reference to observers in my observable implementation, these references must outlive observable itself (so Rust can guarantee refs would be valid).
So here is what I got:
lifetime of observable
|-----------------------|
|--------------------------------------------|
lifetime of observer
This is definitely the opposite of what I wanted to achieve.
It is because observable pattern implies having a circular reference: you should have a reference to observable to subscribe to it’s changes and, vice versa, after you subscribed you should have a reference to observer so you can notify it about the updates.
The most close solution that solves a problem mentioned: https://stackoverflow.com/a/39691337/1730696
It uses Rc
and Weak
. However, the code looks weird and it is definitely not an elegant solution we usually have in Rust.
One of solutions I though about is to have some kind of Subscription struct that automatically unsubscribes when being droped. However, I don’t see any possibility to implement this without using unsafe
code.
Are there any opportunities I missed? Or any other approach that makes possibile to achieve what I want w/o using unsafe
?