How to create and fire event listener

I read couple of article in this forum and outside it about it, but unfortunately did not get how to make it :frowning:

I've the below trait that works fine:

#[derive(Debug)]
pub struct Counter {
 count: i32,
}

trait ManipulateExt {
    fn square(self) -> Self;
    fn increase(self, amount: i32) -> Self;
    fn decrease(self, amount: i32) -> Self;
}

impl ManipulateExt for Counter {
    fn square(self) -> Self { Counter { count: self.count * self.count } }
    fn increase(self, amount: i32) -> Self { Counter { count: self.count + amount } }
    fn decrease(self, amount: i32) -> Self { Counter { count: self.count - amount } }
}

fn main() {
    let mut x = Counter { count: 6 };
    x = x.increase(3);
    println!("{:?}", x);
}

I want to create another trait and implement it to Counter, where this trait's functions are fired as:

trait EventListener {
     fn on_squared() -> () {println!("Counter squared")}
     fn on_increased(amount: i32) -> () {println!("Counter increased by {}", amount)}
     fn on_decreased(self, amount: i32) -> () {println!("Counter reduced from {} to {}", self.count, self.count - amount)}
}

impl EventListener for Counter {}

How can I fire the related EventListener? playground

As an alternative to implementing a trait which redefines some methods, you could look into using the Observer pattern. It lends itself pretty well for reactive use case like yours.

Some links:

From the the reactive rust packge (not sure if it's still maintained)

Note that methods such as fn square(self) take ownership of self, which means the object has either to be copyable (i.e. something trivial like an integer, not much more) or the method will demand to take exclusive ownership of it the object, which will make it difficult to use.

You most likely want to use &self in methods, not self.

impl EventListener for Counter {}

That's backwards. Counter isn't listener, it's an emitter. You implement listeners on other objects which aren't the counter, so that counter can tell them what is happening. You need to make Counter keep a list of objects that are observing it (Vec<Arc<dyn EventListener>>), and notify them about changes.

For listeners it's also pretty much required to wrap listening objects in Arc, otherwise emitter will struggle to refer to them safely.

1 Like

I found this example from here, but did not get how the pub fn on<F>(&mut self, event: T, handler: F) is working, and how it got fired by calling handler(&payload) in the emit

use std::collections::HashMap;
use std::cmp::Eq;
use std::hash::Hash;

#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
enum GreetEvent {
    SayHello,
    SayBye,
}

type HandlerPtr<T> = Box<dyn Fn(&T)>;  // Vec<Arc<dyn EventListener>>; //

pub struct EventEmitter<T: Hash + Eq, U> {
    handlers: HashMap<T, Vec<HandlerPtr<U>>>,
}

impl<T: Hash + Eq, U> EventEmitter<T, U> {
    /// Creates a new instance of `EventEmitter`.
    pub fn new() -> Self {
        Self {
            handlers: HashMap::new(),
        }
    }

    /// Registers function `handler` as a listener for `event`.  There may be
    /// multiple listeners for a single event.
    pub fn on<F>(&mut self, event: T, handler: F)
    where
        F: Fn(&U) + 'static,
    {
        let event_handlers =
            self.handlers.entry(event).or_insert_with(|| vec![]);
        event_handlers.push(Box::new(handler));
    }

    /// Invokes all listeners of `event`, passing a reference to `payload` as an
    /// argument to each of them.
    pub fn emit(&self, event: T, payload: U) {
        if let Some(handlers) = self.handlers.get(&event) {
            for handler in handlers {
                handler(&payload);
            }
        }
    }
}

fn main() {
    let mut emitter = EventEmitter::new();

    emitter.on(GreetEvent::SayHello, |name| {
        println!("Hello, {}!", name);
    });

    emitter.on(GreetEvent::SayHello, |name| {
        println!("Someone said hello to {}.", name);
    });

    emitter.on(GreetEvent::SayBye, |name| {
        println!("Bye, {}, hope to see you again!", name);
    });

    emitter.emit(GreetEvent::SayHello, "Alex");
    emitter.emit(GreetEvent::SayBye, "Alex");
}

The function

pub fn on<F>(&mut self, event: T, handler: F)

is storing a reference to the handler: F (function) in a vector which is inside the a hash-map having the event: T as it it's key.

So when you do:

emitter.on(GreetEvent::SayHello, |name| {
        println!("Hello, {}!", name);
});

The hash-map is updated to look like:

// ref1 is the reference to the anonymous function
// |name| {println!("Hello, {}!", name);}
GreetEvent::SayHello  --> maps to -->  [  ref1  ] 

When the next line is called:

emitter.on(GreetEvent::SayHello, |name| {
        println!("Someone said hello to {}.", name);
});

The hash-map is updated to look like:

// ref2 is the reference to the anonymous function
// |name| { println!("Someone said hello to {}.", name); }
GreetEvent::SayHello  --> maps to -->  [  ref1, ref2  ] 

And then when emit is called:

emitter.emit(GreetEvent::SayHello, "Alex");

We get the vector associated with the event GreetEvent::SayHello and loop through all the function references stored in it and call them one-by-one.

// Get the vector which contains a list of all references 
// to the handler functions against the key `event` 
if let Some(handlers) = self.handlers.get(&event) { key
            // for each function in the vector
            for handler in handlers { 
                // call the function
                handler(&payload); 
            }
}

Thanks @osrust and @kornel
So, can I tell it is all about calling a function Listener from another function Emitter, which can be made in the trait through calling Self::<Fn> something like below playgrund which did the job.

#[derive(Debug)]
pub struct Counter {
 count: i32,
}

trait ManipulateExt {
    fn square(&self) -> Self;
    fn increase(&self, amount: i32) -> Self;
    fn decrease(&self, amount: i32) -> Self;
}

impl ManipulateExt for Counter {
    fn square(&self) -> Self { 
        Self::on_squared();
        Counter { count: &self.count * &self.count } 
    }
    fn increase(&self, amount: i32) -> Self { 
        Self::on_increased(amount);
        Counter { count: &self.count + amount } 
    }
    fn decrease(&self, amount: i32) -> Self { 
        Self::on_decreased(self, amount);
        Counter { count: &self.count - amount } 
    }
}

trait EventListener {
     fn on_squared() -> () {println!("Counter squared")}
     fn on_increased(amount: i32) -> () {println!("Counter increased by {}", amount)}
     fn on_decreased(this: &Counter, amount: i32) -> () {println!("Counter reduced from {} to {}", &this.count, &this.count - amount)}
}

impl EventListener for Counter {}

fn main() {
    let mut x = Counter { count: 6 };
    x = x.increase(3);
    println!("{:?}", x);
    x.decrease(2);
    println!("{:?}", x);
}

Or could be better to re-arrange it as below, so that the Listener be called after executing the Emitter playground

#[derive(Debug)]
pub struct Counter {
 count: i32,
}

trait CounterExt {
    fn square(&mut self);
    fn increase(&mut self, amount: i32);
    fn decrease(&mut self, amount: i32);
}

impl CounterExt for Counter {
    fn square(&mut self) { 
        self.count = self.count * self.count;
        Self::on_squared();
    }
    fn increase(&mut self, amount: i32) { 
        self.count = self.count + amount; 
        Self::on_increased(amount);
    }
    fn decrease(&mut self, amount: i32) {
        let initial_value = self.count;
        self.count = self.count - amount;
        Self::on_decreased(&Counter {count: initial_value}, amount);
    }
}

trait CounterListener {
     fn on_squared() {
        println!("Counter squared")
     }
     fn on_increased(amount: i32) {
        println!("Counter increased by {}", amount)
     }
     fn on_decreased(this: &Counter, amount: i32) {
        println!("Counter reduced from {} to {}", &this.count, &this.count - amount)
     }
}

impl CounterListener for Counter {}

fn main() {
    let mut x = Counter { count: 6 };
    println!("Counter started at: {:#?}", x);
    x.increase(3);
    println!("{:?}", x);
    x.decrease(2);
    println!("{:?}", x);
}

But I've slight issue in the on_decrease as I failed to use self and forced to define another variable that is this: &Counter:

     fn on_decreased(this: &Counter, amount: i32) {
        println!("Counter reduced from {} to {}", &this.count, &this.count - amount)
     }
//  below failed
//     fn on_decreased(self, amount: i32) {
//        println!("Counter reduced from {} to {}", &self.count, &self.count - amount)
//     }

If I defined it as:

fn on_decreased(self, amount: i32) {}

I get error:

error[E0277]: the size for values of type `Self` cannot be known at compilation time
  --> src/main.rs:35:22
   |
35 |      fn on_decreased(self, amount: i32) {
   |                      ^^^^ doesn't have a size known at compile-time
   |

error[E0609]: no field `count` on type `Self`
  --> src/main.rs:36:57
   |
36 |         println!("Counter reduced from {} to {}", &self.count, &self.count - amount)
   |                                                         ^^^^^

And if I tried to define it as:

fn on_decreased(&self, amount: i32) {}

I get the error:

error[E0609]: no field `count` on type `Self`
  --> src/main.rs:36:57
   |
36 |         println!("Counter reduced from {} to {}", &self.count, &self.count - amount)
   |                                                         ^^^^^

The final code worked with me is as per this playground:

//#[derive(Debug)]
pub struct Counter {
 count: i32,
}

impl std::fmt::Debug for Counter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Counter `count` is: {}", self.count)
    }
}

trait EventEmitter {
    fn square(&mut self);
    fn increase(&mut self, amount: i32);
    fn decrease(&mut self, amount: i32);
    fn change_by(&mut self, amount: i32);
}

impl EventEmitter for Counter {
    fn square(&mut self) { 
        self.count = self.count.pow(2);
        Self::on_squared();
    }
    fn increase(&mut self, amount: i32) { 
        self.count += amount; 
        Self::on_increased(amount);
    }
    fn decrease(&mut self, amount: i32) {
        let initial_value = self.count;
        self.count -= amount;
        Self::on_decreased(Self {count: initial_value}, amount);
    }
    fn change_by(&mut self, amount: i32) {
        let initial_value = self.count;
        self.count += amount;
        match amount {
            x if x > 0 => Self::on_increased(amount),
            x if x < 0 => Self::on_decreased(Self {count: initial_value}, amount.abs()),
            _          => println!("No changes")
        }
    }
}

trait EventListener {
     fn on_squared() {
        println!("Counter squared")
     }
     fn on_increased(amount: i32) {
        println!("Counter increased by {}", amount)
     }
     fn on_decreased(self, amount: i32);
}

impl EventListener for Counter {
    fn on_decreased(self, amount: i32) {
        println!("Counter reduced from {} to {}", &self.count, &self.count - amount)
    }
}

fn main() {
    let mut x = Counter { count: 5 };
    println!("Counter started at: {:#?}", x.count);
    x.square();
    println!("{:?}", x);
    x.increase(3);
    println!("{:?}", x);
    x.decrease(2);
    println!("{:?}", x);
    x.change_by(-1);
    println!("{:?}", x);
}

And got the below output:

Counter started at: 5
Counter squared
Counter `count` is: 25
Counter increased by 3
Counter `count` is: 28
Counter reduced from 28 to 26
Counter `count` is: 26
Counter reduced from 26 to 25
Counter `count` is: 25

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.