logoalt Hacker News

ameliustoday at 6:03 PM2 repliesview on HN

Imagine this fairly basic situation: you have a button object. When clicked, it triggers a closure (callback). And that closure needs to change the button's text to e.g. "Clicked!".

To do this in Rust, the closure needs a mutable reference to the button. However, the GUI event loop also needs a reference to the button to draw it on the screen. In Rust, you can have many immutable references, __or__ exactly one mutable reference, but never both at the same time. Since the GUI framework holds the button, it won't let your closure mutably borrow it at the same time.


Replies

veidelistoday at 7:32 PM

Do you find anything "bad" about this code solving the problem?

    use std::rc::Rc;
    use std::cell::RefCell;
    
    struct Button {
        text: String,
        on_click: Option<Rc<dyn Fn()>>,
    }
    
    impl Button {
        fn new(text: &str) -> Self {
            Button { text: text.to_string(), on_click: None }
        }
        fn draw(&self) { println!("[Button: \"{}\"]", self.text); }
        fn fire_click(button: &Rc<RefCell<Button>>) {
            let cb = button.borrow().on_click.clone();
            if let Some(cb) = cb {
                cb();
            }
        }
    }
    
    fn main() {
        let button = Rc::new(RefCell::new(Button::new("Click me")));
        let cb_handle = Rc::clone(&button);
    
        button.borrow_mut().on_click = Some(Rc::new(move || {
            cb_handle.borrow_mut().text = "Clicked!".to_string();
        }));
    
        button.borrow().draw();
        Button::fire_click(&button);
        button.borrow().draw();
    }
Prints:

  [Button: "Click me"]
  [Button: "Clicked!"]
CyberDildonicstoday at 8:27 PM

Why would they need them at the same time? The overall state will contain the button.

Many interactive programs can be GetInput(); ChangeState(); DrawState();

Why wouldn't their lifetimes be isolated?

Also the drawing doesn't need a mutable reference.