Skip to content
Sven edited this page Jul 13, 2020 · 2 revisions

Observer Pattern in Rust

The first page is my now unused Observer pattern in Rust: It's a borrow (not consume) based observer for trait restricted observers with sized data to sent.

The link and the content below is an example of how to use plus test:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=09cbace99fae84e547c8940a703ee377

/// observer pattern
/// idea from https://github.com/lpxxn/rust-design-pattern but extended to box for reference
/// and without detaching
use std::{sync::mpsc, boxed::Box, thread};

pub trait IBorrowingObserver<T: Sized> {
    fn update(&self, data: &T);
}

pub trait IBorrowingObserverNotifier<'a, T: Sized> {
    fn attach(&mut self, observer: Box<&'a dyn IBorrowingObserver<T>>);
    fn notify_observers(&self, data: T);
}

struct NotifyingStruct<'a> {
    observers: Vec<Box<&'a dyn IBorrowingObserver<ConcreteData>>>,
}
impl<'a> NotifyingStruct<'a> {
    pub fn new() -> Self {
        NotifyingStruct {
           observers: Vec::new(),
        }
    }
}

struct ConcreteObserver {
    id: u32,
}
impl IBorrowingObserver<ConcreteData> for ConcreteObserver {
    fn update(&self, data: &ConcreteData) {
        println!(
            "Observer id:{} received event with data {}!",
            self.id, data.content
        );
        let sender = data.sender.clone();
        sender.send(self.id as u64 + data.content).unwrap();
    }
}

struct AnotherConcreteObserver {
    id: u32,
}
impl IBorrowingObserver<ConcreteData> for AnotherConcreteObserver {
    fn update(&self, data: &ConcreteData) {
        println!(
            "Another observer id:{} received event with data {}!",
            self.id, data.content
       );
       let sender = data.sender.clone();
       sender.send(self.id as u64 + data.content * 2).unwrap();
   }
}

impl<'a> IBorrowingObserverNotifier<'a, ConcreteData> for NotifyingStruct<'a> {
    fn attach(&mut self, observer: Box<&'a dyn IBorrowingObserver<ConcreteData>>) {
        self.observers.push(observer);
    }
    fn notify_observers(&self, data: ConcreteData) {
        for item in self.observers.iter() {
           item.update(&data);
        }
    }
}
// must be sized
struct ConcreteData {
    content: u64,
    sender: mpsc::Sender<u64>,
}

fn main() {
    let mut notifier = NotifyingStruct::new();
    let observer_a = ConcreteObserver { id: 1 };
    let observer_b = ConcreteObserver { id: 2 };
    let observer_c = AnotherConcreteObserver { id: 3 };

    let boxed_observer_a: Box<&dyn IBorrowingObserver<ConcreteData>> = Box::new(&observer_a);
    let boxed_observer_b: Box<&dyn IBorrowingObserver<ConcreteData>> = Box::new(&observer_b);
    let boxed_observer_c: Box<&dyn IBorrowingObserver<ConcreteData>> = Box::new(&observer_c);
    
    notifier.attach(boxed_observer_a);
    notifier.attach(boxed_observer_b);
    notifier.attach(boxed_observer_c);
    
    let (sender, receiver) = mpsc::channel::<u64>();
    
    let data = ConcreteData {
        content: 42 as u64,
        sender,
    };

    let test_thread = thread::spawn(move || {
        let tester = move |expected: u64| {
            let received = receiver.recv().unwrap();
            assert_eq!(expected, received, "expected {} got {}", expected, received);
        };
        tester(1 + 42);
        tester(2 + 42);
        tester(3 + 42 * 2);
    });
    
    notifier.notify_observers(data);
    test_thread.join().unwrap();
}

Clone this wiki locally