How can I correctly implement observer pattern in Rust?

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.

1 Like

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.

cross post on stackoverflow: How can I implement the observer pattern in Rust? - Stack Overflow

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

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.

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

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

1 Like

rust design patterns https://github.com/lpxxn/rust-design-pattern

trait IObserver {
    fn update(&self);
}

trait ISubject<'a, T: IObserver> {
    fn attach(&mut self, observer: &'a T);
    fn detach(&mut self, observer: &'a T);
    fn notify_observers(&self);
}

struct Subject<'a, T: IObserver> {
    observers: Vec<&'a T>,
}
impl<'a, T: IObserver + PartialEq> Subject<'a, T> {
    fn new() -> Subject<'a, T> {
        Subject {
            observers: Vec::new(),
        }
    }
}

impl<'a, T: IObserver + PartialEq> ISubject<'a, T> for Subject<'a, T> {
    fn attach(&mut self, observer: &'a T) {
        self.observers.push(observer);
    }
    fn detach(&mut self, observer: &'a T) {
        if let Some(idx) = self.observers.iter().position(|x| *x == observer) {
            self.observers.remove(idx);
        }
    }
    fn notify_observers(&self) {
        for item in self.observers.iter() {
            item.update();
        }
    }
}

#[derive(PartialEq)]
struct ConcreteObserver {
    id: i32,
}
impl IObserver for ConcreteObserver {
    fn update(&self) {
        println!("Observer id:{} received event!", self.id);
    }
}

fn main() {
    let mut subject = Subject::new();
    let observer_a = ConcreteObserver { id: 1 };
    let observer_b = ConcreteObserver { id: 2 };

    subject.attach(&observer_a);
    subject.attach(&observer_b);
    subject.notify_observers();

    subject.detach(&observer_b);
    subject.notify_observers();
}

output:

Observer id:1 received event!
Observer id:2 received event!
Observer id:1 received event!