Update Struct Instance when using Clone

My goal is to instantiate a struct from a parent struct and then have the parent struct call the child's "render" method. The problem is when I want to update field members of the child struct I have to use "clone" (or this is the only way I have found to make the code compile due to a RustWasm event handler closure requirement) to "set_state". The result is the instance of MainChild being held in the Main struct never gets updated.

I am using RustWasm and passing a closure to an event handler in the real code but I simplified the code here to imitate what I see without using RustWasm.

use std::{thread, time};
pub struct Main {
    child: MainChild,
    count: i32,
}

impl Main {
    pub fn create() -> Self {
        Main {
            child: MainChild::create(),
            count: 0,
        }
    }

    pub fn set_state(&mut self, new_count: i32) {
        self.count += new_count;
        self.render();
    }

    pub fn render(&mut self) {
        let mut clone = self.clone();
        self.child.render();
        println!("Hi, From Main === {:?}", self.child);
        if self.count < 1 {
            let five_seconds = time::Duration::from_millis(5000);
            thread::sleep(five_seconds);
            clone.set_state(1);
        }
    }
}

#[derive(Debug, Default, Clone)]
pub struct MainChild {
    count: i32,
}

impl MainChild {
    pub fn create() -> Self {
        MainChild { count: 0 }
    }

    pub fn set_state(&mut self, new_count: i32) {
        self.count += new_count;
        self.render();
    }

    pub fn render(&mut self) {
        println!("Hi, From MainChild  === {:?}", self);
        let mut clone = self.clone();
        if self.count < 1 {
            clone.set_state(5);
        }
    }
}

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

When I do not use "clone" and instead use "self.set_state" the code works as expected. However, I am using "clone" as a solution for this error


      mouse: Some(Box::new(move || self.set_state(2))),
   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 67:5...
  --> src/lib.rs:67:5
   |
67
   | |_____^
note: ...so that the types are compatible
  --> src/lib.rs:89:38
   |
89 |                 mouse: Some(Box::new(move || self.set_state(2))),
   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected  `&Main`
              found  `&Main`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
  --> src/lib.rs:89:24
   |
89 |                 mouse: Some(Box::new(move || self.set_state(2))),
   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected  `std::option::Option<std::boxed::Box<(dyn std::ops::FnMut() + 'static)>>`
              found  `std::option::Option<std::boxed::Box<dyn std::ops::FnMut()>>`

Both of these works:

impl Main {
    pub fn render(&mut self) {
        self.child.render();
        println!("Hi, From Main === {:?}", self.child);
        if self.count < 1 {
            let five_seconds = time::Duration::from_millis(5000);
            thread::sleep(five_seconds);
            self.set_state(1);
        }
    }
}
impl MainChild {
    pub fn render(&mut self) {
        println!("Hi, From MainChild  === {:?}", self);
        if self.count < 1 {
            self.set_state(5);
        }
    }
}

I don't see the code in your error message anywhere. Perhaps you can post that too? My general impression is that the issue appears to be the use of callbacks. Rust is not a garbage collected language, and things must have a single owner. If you want to share ownership with a callback, you must use something like Rc or Arc.

Sorry, trying to to only post the code related to the error so I don't make it too confusing to look at.


   let on_click Some(Box::new(move || self.set_state(2)));
 // self.set_state gives me the error I originally posted. But clone.set_state works. 


   let closure = Closure::wrap(Box::new(move || on_click()) as Box<dyn FnMut()>);
                dom_el
                    .dyn_ref::<HtmlElement>()
                    .expect("should be an `HtmlElement`")
                    .set_onclick(Some(closure.as_ref().unchecked_ref()));
                closure.forget();

The reason it works with a clone is that the closure can take ownership of the clone. Of course, any changes to the clone are not reflected in the original.

Can you give some more details on what you want to do, and perhaps some more complete code? It is not super clear right now, and your last code block is not valid code.

Is this more helpful?

pub struct Props {
    pub children: Option<Vec<Element>>,
    pub text: Option<String>,
    pub on_click: Option<Box<dyn FnMut() -> ()>>,
    pub mouse: Option<Box<dyn FnMut() -> ()>>,
    pub class_name: Option<String>,
    pub id: Option<String>,
}

#[derive(Debug, Default, Clone)]
pub struct Main {
    props: i32,
    child: MainChild,
    id: String,
    count: i32,
}

impl Main {
    pub fn set_state(&mut self, new_count: i32) {
        log(&format!("Hi, From Main Set State {:?}", self.child));
        self.count += new_count;

        ui_library::re_render(self.render(), Some(self.id.clone()));
    }
}

impl ui_library::Component for Main {
    type Properties = i32;
    type Message = String;

    fn create(props: Self::Properties) -> Self {
        log(&format!("create Main"));
        Main {
            props,
            id: "main".to_owned(),
            child: MainChild::create(5),
            ..Default::default()
        }
    }

    fn render(&self) -> ui_library::Element {
        let mut clone = self.clone();
        log(&format!("Hi, From Main {:?}", self.child));
        let main_text = ui_library::create_element(
            "TEXT_ELEMENT".to_owned(),
            ui_library::Props {
                text: Some(format!("Hi, From Main {}", self.count.to_string())),
                ..Default::default()
            },
        );

        let main = ui_library::create_element(
            "div".to_owned(),
            ui_library::Props {
                id: Some(self.id.clone()),
                mouse: Some(Box::new(move || clone.set_state(2))),
                class_name: Some("main".to_owned()),
                children: Some(vec![main_text, self.child.render()]),
                ..Default::default()
            },
        );

        main
    }
}

#[derive(Debug, Default, Clone)]
pub struct MainChild {
    props: i32,
    count: i32,
    id: String,
}

impl MainChild {
    pub fn set_state(&mut self, new_count: i32) {
        self.count += new_count;
        ui_library::re_render(self.render(), Some(self.id.clone()));
    }
}

impl ui_library::Component for MainChild {
    type Properties = i32;
    type Message = String;

    fn create(props: Self::Properties) -> Self {
        log(&format!("create MainChild"));
        MainChild {
            props,
            id: "main-child".to_owned(),
            ..Default::default()
        }
    }

    fn render(&self) -> ui_library::Element {
        let mut clone = self.clone();
        log(&format!("Hi, From Main Child {:?}", self));
        let main_text = ui_library::create_element(
            "TEXT_ELEMENT".to_owned(),
            ui_library::Props {
                text: Some(format!("Hi, From Main Child {}", clone.count.to_string())),
                ..Default::default()
            },
        );

        let main = ui_library::create_element(
            "div".to_owned(),
            ui_library::Props {
                id: Some(self.id.clone()),
                on_click: Some(Box::new(move || clone.set_state(2))),
                class_name: Some("main-child".to_owned()),
                children: Some(vec![main_text]),
                ..Default::default()
            },
        );

        main
    }
}

And then I have a render function which applies the closure to the dom element

      match element.props.on_click {
            Some(mut on_click) => {
                let closure = Closure::wrap(Box::new(move || on_click()) as Box<dyn FnMut()>);
                dom_el
                    .dyn_ref::<HtmlElement>()
                    .expect("should be an `HtmlElement`")
                    .set_onclick(Some(closure.as_ref().unchecked_ref()));
                closure.forget();
            }
            None => (),
        }

        match element.props.mouse {
            Some(mut mouse) => {
                let closure = Closure::wrap(Box::new(move || mouse()) as Box<dyn FnMut()>);
                dom_el
                    .dyn_ref::<HtmlElement>()
                    .expect("should be an `HtmlElement`")
                    .add_event_listener_with_callback("mouseout", closure.as_ref().unchecked_ref())
                    .expect("could not add event listenter");
                closure.forget();
            }
            None => (),
        }

I guess what I need to do is figure out a way not to have to use "clone" to set_state in MainChild.

Well I still can't paste it into the playground and see the error for myself, but I can at least see the closures now.

If you want to use callbacks, you must use Rc or Arc to share the data between the callback and other code.

When you suggest using Rc or Arc to share the data between the callback and other code I am unsure what data I have to wrap in an Rc or Arc. My thought is

pub struct Main {
    child: Rc<MainChild>,
}

But I could be way off on that.

Forgetting about RustWasm code for now, is it possible to make the playground code I initially posted work with Rc or Arc without using clone ?

Use Rc if your application is single-threaded and Arc if it is multi-threaded. It should be possible to make the code work, yes.

Trying to implement the Rc and RefCell in the playground and am getting an error I'm not sure how to handle

error[E0599]: no method named `render` found for mutable reference `&mut std::rc::Rc<std::cell::RefCell<MainChild>>` in the current scope
  --> src/main.rs:32:46
   |
32 |         self.borrow_mut().child.borrow_mut().render();
   |                                              ^^^^^^ method not found in `&mut std::rc::Rc<std::cell::RefCell<MainChild>>


use std::cell::RefCell;
use std::rc::Rc;
use std::{thread, time};

#[derive(Debug, Default, 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, new_count: i32) {
        use std::borrow::BorrowMut;
        self.borrow_mut().count += new_count;
        self.borrow_mut().render();
    }

    pub fn render(&mut self) {
        use std::borrow::BorrowMut;
        // println!(
        //     "Hi, From MainChild  === {:?}",
        //     self.borrow_mut().child.borrow_mut()
        // );
        self.borrow_mut().child.borrow_mut().render();
    }
}

#[derive(Debug, Default, Clone)]
pub struct MainChild {
    pub count: i32,
}

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

    pub fn set_state(&mut self, new_count: i32) {
        use std::borrow::BorrowMut;

        self.borrow_mut().count += new_count;
        self.borrow_mut().render();
    }

    pub fn render(&mut self) {
        use std::borrow::BorrowMut;

        println!("Hi, From MainChild  === {:?}", self);
    }
}

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

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.