Avba
Avba

Reputation: 15296

how to work with a vector of weak references in rust

I want to to hold array of objects to callback to once some event occurs. I don't need to own the objects and in the case the objects were deallocated that is fine, I can remove the listeners lazily from the container.

conceptually something like this but getting lots of compile/ownership errors:

trait EventListener {
  fn event_occured(&mut self, event_name: String); 
}

struct Listener {
  listeners : Vec<Weak<EventListener>>
}

impl Listener {
   fn observe_event(&mut self, listener: impl EventListener) {
      self.listeners.push(Weak(listener)); // how can i box the listener in a weak container
   }
   fn listen_for_events() {

      ... did something here and figured it should broadcast to all listeners ... 
      for listener in self.listeners {
          if listener.is_alive { listener.event_occured(event)} ;  // how to check the weak ref is valid?
      }
   }
}

Upvotes: 0

Views: 1796

Answers (2)

kmdreko
kmdreko

Reputation: 60493

Here's a functional example based on your posted code (on the playground):

use std::rc::{Rc, Weak};

trait EventListener {
  fn event_occured(&self, event_name: &String); 
}

struct Listener {
  listeners : Vec<Weak<dyn EventListener>>
}

impl Listener {
   fn observe_event(&mut self, listener: &Rc<dyn EventListener>) {
      self.listeners.push(Rc::downgrade(listener));
   }
   
   fn listen_for_events(&self) {
        let event = String::new();
        for listener in &self.listeners {
            if let Some(strong_listener) = listener.upgrade() {
                strong_listener.event_occured(&event);
            }
        }
    }
}

struct A {}
impl EventListener for A {
    fn event_occured(&self, _event_name: &String) {
        println!("A");
    }
}

struct B {}
impl EventListener for B {
    fn event_occured(&self, _event_name: &String) {
        println!("B");
    }
}

fn main() {
    let a: Rc<dyn EventListener> = Rc::new(A{});
    let b: Rc<dyn EventListener> = Rc::new(B{});
    
    let mut listener = Listener { listeners: vec![] };
    
    listener.observe_event(&a);
    listener.observe_event(&b);
    
    listener.listen_for_events();
    
    // B goes out of scope
    drop(b);
    
    listener.listen_for_events();
}

it prints

A
B
A

It seems you have some confusion of when Weak can be used. It is not a mechanism to now when just anything is deallocated. It only works when paired with Rc.

Upvotes: 1

user1937198
user1937198

Reputation: 5358

The owner needs to collaborate with this code to allow it to hold weak references. This means that Listener::observe_event needs to take either a Weak<dyn EventListener> or an Rc<dyn EventListener>. (Or the Arc equivelents) At the moment its taking an EventListener value which will of course be deallocated when Listener::observe_event returns.

Upvotes: 1

Related Questions