Check if value still exists in memory

Hi there,
I'm currently dealing with a problem that requires me to pass a value that lives for a short time to a function that only takes 'static values. The problem is the following:

I have a structure representing a web framework, which lives for a limited time. This framework adds event listeners to DOM elements by calling stdweb's add_event_listener method, which requires a 'static closure.

The fact is, as the structure may not live for 'static, I need to be able to "wrap" the closure into an object that could try to get the closure if it still exists in memory.

This means that I would need something like this:

struct LifetimeWrapper<T> {
  fn get_value_if_still_alive(&self) -> Option<&T>;
}

Does such a type exist? I looked for std::rc::Weak<T>, but it seems I cannot use it with limited lifetimes.

EDIT: To clarify a bit, my function looks like this (it's more complicated than this but I only kept the essential):

pub fn handle_events<F: FnMut(Box<dyn Any>)>(elements: Vec<Element>, handle: F) {
    let handle = Rc::new(handle);

    for element in elements {
        let maybe_handle = Rc::downgrade(&handle);

        element.add_event_listener(|click: ClickEvent| {
            match maybe_handle.upgrade() {
                Some(handle) => handle(Box::new("Yes \\o/")),
                None => stdweb::console!(error, "Handler is not alive anymore :(")
            }
        });
    }
}

I get an error telling me that F may not live long enough.

Thanks in advance for your answers :slight_smile:

This is not possible in safe Rust, because it could directly cause eg use-after-free issues.
Requiring values with a 'static lifetime is quite restrictive, in the case of closures it essentially means that the closure can only close over values that exist for the lifetime of the program itself.

Check this link to the playground for some examples of what does and doesn't work.

This is not possible in safe Rust, because it could directly cause eg use-after-free issues.

How could it? The type I'm looking for would check if the data is still alive (= has not been freed), which would resolve the problem of use-after-free.

EDIT: I've drawn the same conclusions you show in the playground, but the way the program works the closure must have the 'static lifetime and still use variables that do not live as long.

Safe Rust uses the type and borrow checkers and they have no way of figuring out that that is the case.

In unsafe Rust you may be able to get away with something like that, but that is because the burden falls on you, the programmer, to prove that your code is sound.

BTW, cloning the state is not an option for you?
If not, then the only way to get what you want may be unsafe code.

The variable used by the closure is a structure representing a page, which often contains non-clonable data, so cloning isn't an option unfortunately.

I can't replicate the error, but

So, any variable closed over by the closure has to be of a type that satisfies 'static...

pub fn handle_events<F: 'static + FnMut(Box<dyn Any>)>(elements: Vec<Element>, handle: F) {
//                       ^^^^^^

And you need to move the closed-over variable (maybe_handle) into the closure, so it doesn't store a non-'static reference...

        element.add_event_listener(move |click: ClickEvent| {
        //                         ^^^^

And then you're going to have to figure out how to call the function inside the Rc, because you need a &mut F to call a FnMut and Rc only offers shared references. The easy thing to do is use Fn instead of FnMut (you could consider putting it in a RefCell instead).

pub fn handle_events<F: 'static + Fn(Box<dyn Any>)>(elements: Vec<Element>, handle: F) {
//                                ^^

This compiles (with some dummy APIs because the playground doesn't support your dependencies).

In this case, nobody is hanging on to an Rc to handle. So as soon as handle_events returns, all those Weaks are useless. Presumably your real code is different.

This indeed compiles, but the closure cannot be static because it moves non-static references to data. Which is why I'm blocked at the moment ^^.

After looking through the different types Rust offers (Rc, RefCell, Weak, Arc, etc.) is seems that it's indeed not possible without using unsafe code :frowning:

Thanks anyway for your help, folks :slight_smile:

Then that data is what you need to put in an Rc, right?

I showed you how to fix the problem with handle_events; if the caller has a similar problem, I would bet you can solve it in a similar way.

Another option might be to use scoped threads, such as provided by crossbeam or scoped_threadpool. Oops, nevermind, I momentarily forgot you're using an API that requires 'static explicitly.

It's not possible with unsafe code either. There is no way to check if a pointer is still valid. It has to be owned by the smart point for it to be able to determine if it still exists.

2 Likes

Nice! Putting the page object in an Rc<RefCell<T>> works perfectly!
This allows me to simply use this object without having to create manually a mutable reference that may be dropped too soon.

Thanks for your help :smiley: