#Lifetimes error even if dropped

46 messages · Page 1 of 1 (latest)

torn heath
#

I am not sure why it shows the error even if handler will be dropped before call_tracker I tried to play with scope and follow the E0597 instruction but no clue why still errors.

use std::any::Any;

pub trait EventHandler {
    fn handle(&self, event: &dyn Any);
}

pub enum EventKey {
    ProjectUpdated,
}

pub enum Event {
    ProjectUpdated(String),
}

pub struct EventBus {
    handlers: Vec<Box<dyn EventHandler>>,
}

impl EventBus {
    pub fn new() -> Self {
        Self { handlers: Vec::new() }
    }

    pub fn register(&mut self, key: EventKey, handler: Box<dyn EventHandler>) {
        self.handlers.push(handler);
    }

    pub fn emit(&self, key: EventKey, event: Event) {
        for handler in &self.handlers {
            handler.handle(&event);
        }
    }
}

mod tests {
    use std::{cell::RefCell, rc::Rc};

    use super::*;

    struct MyHandler<'a> {
        // calls: Rc<RefCell<u8>>,
        calls: &'a RefCell<u8>,
    }

    impl EventHandler for MyHandler<'_> {
        fn handle(&self, event: &dyn Any) {
            *self.calls.borrow_mut() += 1;
        }
    }

    #[test]
    fn can_create_event_bus() {
        let call_tracker: RefCell<u8> = RefCell::new(0);
        {
            let handler = MyHandler { calls: &call_tracker };
            {
                let mut bus = EventBus::new();
                bus.register(EventKey::ProjectUpdated, Box::new(handler));
                bus.emit(EventKey::ProjectUpdated, Event::ProjectUpdated("test".to_string()));
            }

            assert_eq!(*call_tracker.borrow(), 1);
        }
    }
}
#

here is MyHandler definition

barren raptor
#

first off

#

-code

late umbraBOT
#

Please post your code examples and compiler output with code fences (```) around them. Example:

```rust
let (x, y) = (0, 42);
println!("Position at {}, {}", x, y);
```
let (x, y) = (0, 42);
println!("Position at {}, {}", x, y);

If the snippet is long or you want to demonstrate something, consider sharing it through the playground: https://play.rust-lang.org/ or https://www.rustexplorer.com/ or https://paste.rs/web.

Please avoid sharing screenshots of your code, as they're not very accessible. Using code fences or a shared snippet makes the code more readable and allows those helping you to copy-paste the code to help explain things.

barren raptor
#

besides that, the problem is likely that bus.register has stronger lifetime requirements

#

which we can kinda see in the error message, but it's cut off

#

-errors

late umbraBOT
#

Run cargo check in a terminal

Note: If using rust analyzer you can click the "click for full compiler diagnostic" link in your editor.

Please post the full output of the above command, including the error title and any help or notes. An example of how this looks is:

error[E0308]: mismatched types
 --> src/main.rs:3:17
  |
3 | let foo: &i32 = bar;
  |          ----   ^^^ expected `&i32`, found integer
  |          |
  |          expected due to this
  |
help: consider borrowing here
  |
3 | let foo: &i32 = &bar;
  |                 +

When posting the error put it in a code block so it has nice formatting:
```rust
// error from cargo check here
```

Please do not post a screenshot. If the output is to long then use a paste tool like https://paste.rs/web

barren raptor
#

have you read the error message as well?

#

there's a very useful note at the end of it

#

basically, the problem is that register expects to have ownership over the handler, so the handler needs to live possibly forever

torn heath
#
error[E0597]: `call_tracker` does not live long enough
  --> src/project/event_bus.rs:57:37
   |
55 |         let call_tracker: RefCell<u8> = RefCell::new(0);
   |             ------------ binding `call_tracker` declared here
56 |         {
57 |             let handler = MyHandler { calls: &call_tracker };
   |                                              ^^^^^^^^^^^^^ borrowed value does not live long enough
...
60 |                 bus.register(EventKey::ProjectUpdated, Box::new(handler));
   |                                                        ----------------- cast requires that `call_tracker`
 is borrowed for `'static`
...
66 |     }
   |     - `call_tracker` dropped here while still borrowed
   |
   = note: due to object lifetime defaults, `Box<dyn event_bus::EventHandler>` actually means `Box<(dyn event_b
us::EventHandler + 'static)>`

I get that

   = note: due to object lifetime defaults, `Box<dyn event_bus::EventHandler>` actually means `Box<(dyn event_b
us::EventHandler + 'static)>`

but the EventBus is a separate structure, and MyHandler is the actual one the rquires a field with lifetime

barren raptor
#

no, that's a misunderstanding

#

rust tries to create a MyHandler<'static> because of the requirements of register

torn heath
#

then how to solve this ? change the signautre of register ? and handler already dropped no clue why still complaining

barren raptor
#

you could fix this by changing your code to

pub struct EventBus<'a> {
    handlers: Vec<Box<dyn EventHandler + 'a>>,
}

impl<'a> EventBus<'a> {
    pub fn register(&mut self, key: EventKey, handler: Box<dyn EventHandler + 'a>) {
        self.handlers.push(handler);
    }
}
#

admittedly this is a bit annoying

torn heath
#

sematiclly my code is right? but compiler cannot determine the lifetime of register right?
and that's why I need to write the lifetime for register explicitly ?
this what I understand of the code you sent

barren raptor
#

the compiler sees that you require an owned handler, so it enforces that

#

when the bus is dropped, doesn't really matter

#

because, for example, register could be attaching the handler to some global variable that lives longer than the EventBus

#

looking at your entire code, it's more or less correct. however, looking at only the function signatures, it's not

torn heath
barren raptor
#

you can fix this either in EventBus, making it lifetime aware

#

or you fix this by using an Rc<RefCell<u8>> instead

torn heath
#

appreciated 🫡 now it got fixed and I understood the original issue

barren raptor
#

fwiw, I find it curious that emit only takes an &self instead of an &mut self

#

or rather, I'm surprised at EventHandler::handle(&self) instead of EventHandler::handle(&mut self)

torn heath
#

I didn't get what you mean?, I don't mutate any state within emit

barren raptor
#

you do in handle

torn heath
#

becuase self.calls is &'a RefCell<u8> i guess

barren raptor
#

i mean, interior mutability isn't wrong per-se, but I'm curious why you require all handlers to use interior mutability

torn heath
#

what I am trying to do, is handler should not mutate any value for self by default but I used the RefCell for testing the register & emit behavior

What I am trying to do is observer pattern. and each handler responsible about changing it's state. but I get your point now I restrict the handler to change any state unless if it behind RefCell

I am not sure if what I am doing right in terms of design but I am new to rust I am sure that I missed stuff

barren raptor
#

yeah it's not necessarily wrong, i just find it odd

torn heath
#

What would suggest instead? I would appreciate if you give example or reference

barren raptor
#

depends a bit on what you're going for

#

Do you plan to share the EventEmitter with multiple owners?
If so, is that a fundamental property of it? Being shared or not?

torn heath
#

I am trying basically to do observer pattern type/event name => many handlers
And EventBus only one instance that would be passed to other services.

barren raptor
#

If you're not planning to share it:
-> change emit to use &mut self
If you're planning to share it, but it's not a fundamental property
-> change emit to use &mut self and use Rc<RefCell<EventBus>> to share it
If sharing it is a fundamental property, this might be a bit more involved

torn heath
#

got it, thank you for the feedback

barren raptor
#

if it's the last point, the question becomes whether register should also be shared around. Probably not, since it takes an &mut.

#

in which case, maybe a builder pattern would be nicer, yada yada you see where I'm going

torn heath
#

yes, I will work on it, I am learning the interior mutability + lifetimes. ofc I will try to re-evaluate the design in every steps. and your points made me think about important stuff down the road for the actual usage of EventBus