Pass Closure from Parent to Child

I'm trying to pass a closure allowing a struct to update its parent struct. The requirement is the closure needs to be passed from the parent to the child, then to a Rust Wasm event handler which expects the closure to be of type Box<dyn FnMut() -> ()>.

Receiving multiple errors in several different attempts but the latest indicates my "borrow does not live long enough".

use std::cell::RefCell;
use std::ops::DerefMut;
use std::rc::Rc;

#[derive(Clone)]
pub struct Main {
    pub child: Rc<RefCell<MainChild>>,
    pub count: i32,
}

impl Main {
    pub fn create() -> Rc<RefCell<Self>> {
        let main = Main {
            child: MainChild::create(),
            count: 0,
        };
        Rc::new(RefCell::new(main))
    }

    pub fn set_state(&mut self) {
        self.count = 50;
    }

    pub fn render(&mut self) {
        let child = &*self.child;
        let mut clone = self.clone();
        child.borrow_mut().closure = Rc::new(RefCell::new(Box::new(move || clone.set_state())));
        child.borrow_mut().render();
    }
}

#[derive(Clone)]
pub struct MainChild {
    pub closure: Rc<RefCell<Box<dyn FnMut() -> ()>>>,
}

impl MainChild {
    pub fn create() -> Rc<RefCell<Self>> {
        let closure = Box::new(|| println!("hi")) as Box<dyn FnMut() -> ()>;
        let child = MainChild {
            closure: Rc::new(RefCell::new(closure)),
        };
        Rc::new(RefCell::new(child))
    }

    pub fn render(&mut self) {
        //  Need new_closure to be passed to an async Rust Wasm event handler.
        //  Event Handler expects Box<dyn FnMut() -> ()>

        let closure = &self.closure;
        let closure_clone = Rc::clone(&closure);
        let mut reference = closure_clone.borrow_mut();
        let deref = reference.deref_mut();
        let on_click = Box::new(move || deref());
        mock_rust_wasm_event_handler(on_click);
    }
}

pub fn mock_rust_wasm_event_handler(callback: Box<dyn FnMut() -> ()>) {
    ()
}

pub fn main() {
    let main = Main::create();
    main.borrow_mut().render();
}

Here is another attempt where I use a wrapper on the structs. Still getting the same error of

error[E0597]: `closure_clone` does not live long enough
  --> src/main.rs:64:29
   |
64 |         let mut reference = closure_clone.borrow_mut();
   |                             ^^^^^^^^^^^^^ borrowed value does not live long enough
...
67 |         mock_rust_wasm_event_handler(on_click);
   |                                      -------- cast requires that `closure_clone` is borrowed for `'static`
68 |     }
   |     - `closure_clone` dropped here while still borrowed

error[E0597]: `reference` does not live long enough
  --> src/main.rs:65:21
   |
65 |         let deref = reference.deref_mut();
   |                     ^^^^^^^^^ borrowed value does not live long enough
66 |         let on_click = Box::new(move || deref());
67 |         mock_rust_wasm_event_handler(on_click);
   |                                      -------- cast requires that `reference` is borrowed for `'static`
68 |     }
   |     - `reference` dropped here while still borrowed

use std::cell::RefCell;
use std::ops::DerefMut;
use std::rc::Rc;

#[derive(Debug, Default, Clone)]
pub struct Handle<T>(pub Rc<RefCell<T>>);

pub trait Component: Sized + 'static {
    fn render(&mut self);
    fn set_state(&mut self);
}

#[derive(Clone)]
pub struct Main {
    pub child: Handle<MainChild>,
    pub count: i32,
}

impl Main {
    pub fn create() -> Handle<Self> {
        let main = Main {
            child: MainChild::create(),
            count: 0,
        };
        Handle(Rc::new(RefCell::new(main)))
    }
}

impl Component for Handle<Main> {
    fn set_state(&mut self) {
        self.0.borrow_mut().count = 50;
    }

    fn render(&mut self) {
        let mut clone = self.clone();
        let mut child = self.0.borrow_mut().child.clone();
        child.0.borrow_mut().closure = Rc::new(RefCell::new(Box::new(move || clone.set_state())));
        child.render();
    }
}

#[derive(Clone)]
pub struct MainChild {
    pub closure: Rc<RefCell<Box<dyn FnMut() -> ()>>>,
}

impl MainChild {
    pub fn create() -> Handle<Self> {
        let closure = Box::new(|| println!("hi")) as Box<dyn FnMut() -> ()>;
        let child = MainChild {
            closure: Rc::new(RefCell::new(closure)),
        };
        Handle(Rc::new(RefCell::new(child)))
    }
}

impl Component for Handle<MainChild> {
    fn render(&mut self) {
        //  Need new_closure to be passed to an async Rust Wasm event handler.
        //  Event Handler expects Box<dyn FnMut() -> ()>

        let closure = &self.0.borrow_mut().closure;
        let closure_clone = Rc::clone(&closure);
        let mut reference = closure_clone.borrow_mut();
        let deref = reference.deref_mut();
        let on_click = Box::new(move || deref());
        mock_rust_wasm_event_handler(on_click);
    }

    fn set_state(&mut self) {
        ()
    }
}

pub fn mock_rust_wasm_event_handler(callback: Box<dyn FnMut() -> ()>) {
    ()
}

pub fn main() {
    let mut main = Main::create();
    main.render();
}

Not sure if this is going to work in your real code, but the problem I believe is that there are no restrictions on what mock_rust_wasm_event_handler can do with the reference, so it has an implicit 'static bound. I made your example compile by adding an explicit lifetime parameter:

pub fn mock_rust_wasm_event_handler<'a>(callback: Box<dyn 'a + FnMut() -> ()>) {

Yes, that does make the code given work. When I apply it to my original code I get

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
   --> src/rustact.rs:129:45
    |
129 |                 let closure = Closure::wrap(Box::new(move || on_click()) as ClosureProp<'a>);
    |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 104:15...
   --> src/rustact.rs:104:15
    |
104 | pub fn render<'a>(rustact_element: Element<'a>, container: &web_sys::Node, replace_child: bool) {
    |               ^^
note: ...so that the expression is assignable
   --> src/rustact.rs:129:45
    |
129 |                 let closure = Closure::wrap(Box::new(move || on_click()) as ClosureProp<'a>);
    |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: expected  `std::boxed::Box<dyn std::ops::FnMut()>`
               found  `std::boxed::Box<(dyn std::ops::FnMut() + 'a)>`
    = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
   --> src/rustact.rs:129:31
    |
129 |                 let closure = Closure::wrap(Box::new(move || on_click()) as ClosureProp<'a>);
    |                               ^^^^^^^^^^^^^
    = note: expected  `wasm_bindgen::closure::WasmClosure`
               found  `wasm_bindgen::closure::WasmClosure`

This is my signature for ClosureProp

pub type ClosureProp<'a> = Box<dyn 'a + FnMut() -> ()>;

I don't see a way around this error from looking at the docs for but I'm far from experienced with lifetimes.

Yeah, it looks like the closure will have to be 'static.

Closure currently requires that the closures it's created with have the 'static lifetime in Rust for soundness reasons.

To accomplish this, the closure given to the event handler cannot be a reference to something that lives in MainChild. Not sure what the best way to do so is in your case. I think your options are to make the closure held by MainChild 'static by using a fn pointer (something like closure: fn() -> ()), giving the event handler ownership of the closure (by doing something like wrapping the closure in an Option and taking it with Option::take()), or restructuring your code so that the closure isn't stored in MainChild.

Could the closure be passed into the render function for MainChild instead of being stored inside MainChild?

Alternatively, another approach I am not having any luck with is to hold a Weak reference to Handle<Main> inside the MainChild struct like

pub struct MainChild {
   pub parent: RefCell<Weak<Handle<Main>>>
 }

Then trying to call the Main struct's set_state from the MainChild.
Like this approach in the Rust book Reference Cycles Can Leak Memory - The Rust Programming Language
But can't seem to get that right even in the playground yet.

I think that would make the most sense if the real code matches the sample code in the way the closures are used. Currently it's a bit unclear if the closure is meant to be reused by MainChild, or if it's only used in render and always set before it's called.
Unfortunately I haven't had a chance to use Rc in real code so I'm not the best person to give advice regarding it, but my intuition is that it won't help here, in the end the handler still wants an owned closure and you can't get that out of an Rc without consuming it.

This works but with a caveat

use std::cell::RefCell;
use std::ops::DerefMut;
use std::rc::Rc;

#[derive(Debug, Default, Clone)]
pub struct Handle<T>(pub Rc<RefCell<T>>);

pub trait Component: Sized + 'static {
    fn render(&mut self, callback: Option<Box<dyn FnMut() -> ()>>);
    fn set_state(&mut self);
}

#[derive(Clone)]
pub struct Main {
    pub child: Handle<MainChild>,
    pub count: i32,
}

impl Main {
    pub fn create() -> Handle<Self> {
        let main = Main {
            child: MainChild::create(),
            count: 0,
        };
        Handle(Rc::new(RefCell::new(main)))
    }
}

impl Component for Handle<Main> {
    fn set_state(&mut self) {
        self.0.borrow_mut().count = 50;
        println!("{:?}",self.0.borrow_mut().count);
    }

    fn render(&mut self, callback: Option<Box<dyn FnMut() -> ()>>) {
        let mut clone = self.clone();
        let mut child_handle = self.0.borrow_mut().child.clone();
        let callback_for_child = Box::new(move || clone.set_state());
        child_handle.render(Some(callback_for_child));
    }
}

#[derive(Clone)]
pub struct MainChild {}

impl MainChild {
    pub fn create() -> Handle<Self> {
        let child = MainChild {};
        Handle(Rc::new(RefCell::new(child)))
    }
}

impl Component for Handle<MainChild> {
    fn render(&mut self, callback: Option<Box<dyn FnMut() -> ()>>) {
        //  Need new_closure to be passed to an async Rust Wasm event handler.
        //  Event Handler expects Box<dyn FnMut() -> ()>

        mock_rust_wasm_event_handler(callback.unwrap());
    }

    fn set_state(&mut self) {
    // If I wanted to call self.render here after setting the childs internal state
    // This approach would not work because this method doesn't have access to Main's closure
        ()
    }
}

pub fn mock_rust_wasm_event_handler(mut callback: Box<dyn FnMut() -> ()>) {
    callback();
}

pub fn main() {
    let mut main = Main::create();
    main.render(None);
}

If I wanted to have the MainChild set it's own state and then re-render itself, MainChild would no longer have Main's callback closure being passed to it.

I think your options are to make the closure held by MainChild 'static by using a fn pointer (something like closure: fn() -> () ), giving the event handler ownership of the closure (by doing something like wrapping the closure in an Option and taking it with Option::take() )

I'm not sure what you mean here but I'm interested. Do you have a small example piece of code?

I was thinking something like

fn hello() {
    println!("hi!");
}

struct MainChild {
    fn_pointer: fn(),
}

fn main() {
    let main_child = MainChild {
        fn_pointer: hello,
    };
}

or

struct MainChild {
    closure: Option<Box<dyn FnMut()>>,
}

fn main() {
    let mut main_child = MainChild {
        closure: Some(Box::new(|| println!("hi"))),
    };
    if let Some(closure) = main_child.closure.take() {
        // something
    }
}

but I misunderstood the problem a bit and they won't work here.

I looked at the original code again, and another way to make it so the closure doesn't hold references is to give it the cloned Rc and do the borrowing inside it.

    pub fn render(&mut self) {
        let closure = &self.closure;
        let closure_clone = Rc::clone(&closure);
        let on_click = Box::new(move || {
            let mut reference = closure_clone.borrow_mut();
            let deref = reference.deref_mut();
            deref()
        });
        mock_rust_wasm_event_handler(on_click);
    }

But I'm not sure how well this would work in practice either.

1 Like

Try changing from Box<dyn FnMut()> to Rc<dyn Fn()> and using Rc::clone to pass it to the child.

You can then convert it back to a Box<dyn FnMut()> by doing:

Box::new({
    let rc_closure = Rc::clone(...);
    move || rc_closure()
})

or the downgraded version:

Box::new({
    let rc_closure = Rc::downgrade(...);
    move || rc_closure.upgrade().expect("Stale")()
})
1 Like

Looks like this is working in my source code! Can you explain why?

    pub fn render(&mut self) {
    let closure = &self.closure;
    let closure_clone = Rc::clone(&closure);
    let on_click = Box::new(move || {
        let mut reference = closure_clone.borrow_mut();
        let deref = reference.deref_mut();
        deref()
    });
    mock_rust_wasm_event_handler(on_click);
}

wasm_bindgen's Closure wants the Rust closure to have the 'static lifetime, which means that it's not allowed to contain (non-'static) references to outside the closure. By moving the borrow_mut call inside the closure, closure_clone is moved into the closure so the closure owns it (thanks to the move keyword) and it is only then borrowed. In the previous version of the code, the closure was accessing a &mut variable that was outside of the closure, meaning it contained a reference, making it not 'static.

The idea is a bit easier to illustrate with structs

struct Static {
    owned: usize,
}

struct Borrows<'a> {
    borrowed: &'a usize,
}

fn only_takes_static<T: 'static>(t: T) {}
fn takes_any<T>(t: T) {}

fn main() {
    let static_struct = Static { owned: 0 };

    let some_usize = 0;
    let borrows = Borrows {
        borrowed: &some_usize,
    };

    only_takes_static(static_struct);
    only_takes_static(borrows); // causes a compiler error saying some_usize doesn't live long enough
    // takes_any(borrows); // this would be OK
}

The reason I specified non-'static before is that this will actually compile if we make some_usize a constant with const some_usize: usize = 0;, because then the borrow (and the struct) will have a 'static lifetime.

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.