Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I implement the observer pattern in Rust?

I have an observable collection and an observer. I want the observer to be a trait implementation of trait Observer. The observable object should be able to notify each observer when some event occurs. This should explain my intentions:

struct A {     observables: Vec<Observable>, }  impl A {     fn new() -> A {         A {             observables: vec![],         }     } }  trait Observer {     fn event(&mut self, _: &String); }  impl Observer for A {     fn event(&mut self, ev: &String) {         println!("Got event from observable: {}", ev);     } }  struct Observable {     observers: Vec<dyn Observer>, // How to contain references to observers? (this line is invalid) }  impl Observable {     fn new() -> Observable {         Observable {             observers: Vec::new(),         }     }      fn add_observer(&mut self, o: &dyn Observer) {         // incorrect line too         self.observers.push(o);     }      fn remove_observer(&mut self, o: &dyn Observer) {         // incorrect line too         self.observers.remove(o);     }      fn notify_observers(&self, ev: &String) {         for o in &mut self.observers {             o.event(ev);         }     } } 

(Playground)

I get the error:

error[E0277]: the size for values of type `(dyn Observer + 'static)` cannot be known at compilation time   --> src/lib.rs:24:5    | 24 |     observers: Vec<dyn Observer>, // How to contain references to observers?    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time    |    = help: the trait `std::marker::Sized` is not implemented for `(dyn Observer + 'static)`    = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>    = note: required by `std::vec::Vec` 

This is just a mock-up of what I want to do. I have code like this in Java, Python, and C++, but I don't know how to implement the observer pattern in Rust. I believe my problem is in storing a reference to observer objects inside observable objects.

like image 602
Victor Polevoy Avatar asked Jun 01 '16 15:06

Victor Polevoy


People also ask

How do you use observer pattern?

Observer pattern is used when there is one-to-many relationship between objects such as if one object is modified, its depenedent objects are to be notified automatically. Observer pattern falls under behavioral pattern category.

Where is Observer pattern used in real life?

A real world example of observer pattern can be any social media platform such as Facebook or twitter. When a person updates his status – all his followers gets the notification. A follower can follow or unfollow another person at any point of time.

When should Observer Pattern be used?

Observer Pattern is one of the behavioral design pattern. Observer design pattern is useful when you are interested in the state of an object and want to get notified whenever there is any change.

Is the observer pattern good?

Imho the Observer pattern is great for notification of (independent) GUI elements, but the pattern has some serious drawbacks as well: spurious notifications while subject or business layer is not in a consistent state; subject changes by a observer during a notification update.


1 Answers

The Observer pattern, depending on implementation choices, may pose an ownership challenge.

In garbage collected languages it is typical to have the Observable referring to the Observer (to notify it) and the Observer referring to the Observable (to unregister itself)... this causes some challenges in terms of ownership (who outlives whom?) and there is this whole "notification on un-registering" thing.

In Rust (and C++), I advise avoiding cycles.


Simple solution

The Observable and Observer have distinct lifetimes, none owning the other or being expected to outlive the other.

use std::rc::Weak;  struct Event;  trait Observable {     fn register(&mut self, observer: Weak<dyn Observer>); }  trait Observer {     fn notify(&self, event: &Event); } 

The key is to allocate the Observer into a Rc and then hand over Weak (weak references) to the Observable.

If the Observer needs be modified on the Event, then either it needs internal mutability or it needs to be wrapped into a RefCell (passing Weak<RefCell<dyn Observer>> to the Observable).

When notifying, the Observable will regularly realize that there are dead weak-references (the Observer has disappeared), it can remove those then, lazily.


There are other solutions, such as using a Broker (quite similar to an event loop), moving from push mode to pull mode (i.e. (1) generate all events, (2) treat all of them), however these depart a bit from the traditional Observer Pattern and have different pluses/minuses so I will not attempt to treat them all here.

like image 191
Matthieu M. Avatar answered Sep 28 '22 17:09

Matthieu M.