How can I correctly implement observer pattern in Rust?


#1

I have an observable collection and observer. I want observer to be a trait implementation of trait Observer. Observable object should be able to notify each of it’s observer when some event appear. Code below should explain my intentions more detailed:

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<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: &Observer) { // incorrect line too
        self.observers.push(o);
    }

    fn notify_observers(&self, ev: &String) {
        for o in &mut self.observers {
            o.event(ev);
        }
    }
}

This is just a mock-up of what I wanna do. I had a code like this in java, python, cpp but I don’t know how to implement the observer pattern in rust.


Observer pattern in Rust
#2

You’ll either need to store Box<Observer> values, if the observers are entirely owned by Observable, or Rc<Observer> if ownership must be shared with other parts of the code (in which case &mut self won’t work, and each implementation of Observer will require interior mutability using Cell/RefCell). The final option would be &Observer, but that only works if you can guarantee that all observers will outlive the Observable object that stores pointers to them, which seems unlikely given the pattern being implemented.


#3

cross post on stackoverflow: http://stackoverflow.com/questions/37572734/how-can-i-correctly-implement-observer-pattern-in-rust


#4

Why don’t you just pass functions/closures? This object orientated observer pattern is obsolete in functional programming.


#5

I found another option - Pull mode (returning events from function and handling them), that Matthieu M. is talking about on Stackoverflow, cleaner and simpler than observer pattern.


#6

Can you please provide some code that is a result of your application of the pattern?


#7

Sure, simplified example for my particular use-case:

pub trait Aggregate<TEvent>: Default {
  fn apply(&mut self, e: &TEvent);
}

#[allow(non_camel_case_types)]
pub enum Event {
 COUNTER_DECREASED { count: i32, amount: i32 },
 COUNTER_INCREASED { count: i32, amount: i32 },
}

pub struct Counter {
 count: i32,
}

impl Default for Counter {
 fn default() -> Self {
   Counter {
     count: 0
   }
 }
}

impl Aggregate<Event> for Counter {
 fn apply(&mut self, e: &Event) {
   match *e {
     Event::COUNTER_DECREASED { count, .. } => self.count = count,
     Event::COUNTER_INCREASED { count, .. } => self.count = count,
   }
 }
}

impl Counter {
 pub fn decrease(&self, amount: i32) -> [Event; 1] {
   [Event::COUNTER_DECREASED { count: self.count - amount, amount: amount }]
 }

 pub fn increase(&self, amount: i32) -> [Event; 1] {
   [Event::COUNTER_INCREASED { count: self.count + amount, amount: amount }]
 }
}

For handling those events I have a macro, because in my case it is a repetitive task. Hope it helps