How to create callback sender inside listener?

I have the next code:

pub struct Sender<'a> {
    event_callback: Box<dyn FnMut(Event) + 'a>,
}

impl<'a> Sender<'a> {
    pub fn new(callback: impl FnMut(Event) + 'a) -> Self {
        Sender { event_callback: Box::new(callback) }
    }

    pub fn send_event(&mut self, event: Event) {
        (self.event_callback)(event);
    }
}

pub struct Listener<'a> {
    my_bool: bool,
    sender: Option<Sender<'a>>,
}

impl<'a> Listener<'a> {
    pub fn new() -> Self {
        let mut listener = Listener { sender: None, my_bool: false };
        listener.create_sender();
        listener
    }

    fn create_sender(&'a mut self) {
        self.sender = Some(
            Sender::new( |event|
                match event.event_type() {
                    EventType::SomeType => self.my_bool = true,
                    _ => (),
                }
            )
        );
    }
}

Issues are:

  1. Listeners new function does not compile:
  • E0515: cannot return value referencing local variable listener
  • E0505: cannot move out of listener because it is borrowed
    I don't really understand the reason why it wouldn't compile.
  1. If it would compile, there's still this annoying Option<Sender<'a>> which I don't really need because I create Sender immediately after creating listener.

Ideally I would want to write something like this:

    pub fn new() -> Self {
        Listener { window: self.create_sender(), my_bool: false };
    }

How do I implement it? Are there better ways than what I'm trying to do?

You're trying to create a self-referential struct, that is, a struct where one field has a reference to another field (or the entire struct). In this case, the closure will hold a reference to the my_bool field so that it can access it.

Self-referential structs are not possible in safe Rust.

Consider making &mut Listener an argument of the closure, rather than accessing self.

Or consider not using callbacks at all.

As a general tip for this kind of thing, any solution where Listener or Sender has a lifetime annotation will be wrong.

3 Likes

Thanks for the answer!

I think that making &mut Listener an argument of closure looks pretty ugly, besides, I don't want Sender to know anything about Listener. If it wasn't the case I would probably use Observer pattern instead.

And then there's also this problem with Option<Sender> remaining.

What general solutions are there for this kind of problem?

The most general solution is not to try implementing classical object-oriented design patterns in a language that is not object-oriented.

The immediate problem of self-reference can be resolved by using Rc and Weak instead of plain references.

Here's a possible implementation. Checking correctness w.r.t. Weaks living long enough and RefCell borrows not panicking is left as an exercise.

2 Likes

It is generally the case that callbacks are inconvenient to use in Rust, because pretty much all callbacks will need to modify data that is shared with other parts of the program. Shared mutability is difficult in Rust. I recommend not using callbacks at all.

1 Like

Thanks guys. I will use something else.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.