rustevent-handlinglifetimefltk

How to handle the lifetimes of two fltk buttons that toggle eachother?


I'm trying to create two buttons (btn1, btn2) handling events (simple 'push' aka 'onclick'), they should disable each other

let mut btn1 = button::Button::new(100, 100, 100, 50, "BTN1");
let mut btn2 = button::Button::new(300, 100, 150, 50, "BTN2");

// handle btn1 click
btn1.handle(move |thisBtn, evt| match evt {
    enums::Event::Push => {
        clicked(thisBtn, &mut btn2);
        true
    }
    _ => false,
});

// handle btn2 click
btn2.handle(move |thisBtn, evt| match evt {
    enums::Event::Push => {
        clicked(thisBtn, &mut btn1);
        true
    }
    _ => false,
});

// both buttons should be mutable, or activate/deactivate wont work
fn clicked(thisBtn: &mut button::Button, otherBtn: &mut button::Button) {
    thisBtn.deactivate();
    otherBtn.activate();
}
error[E0382]: borrow of moved value: `btn2`
  --> src\main.rs:18:5
   |
6  |       let mut btn2 = button::Button::new(300, 100, 150, 50, "BTN2");
   |           -------- move occurs because `btn2` has type `Button`, which does not implement the `Copy` trait
...
9  |       btn1.handle(move |thisBtn, evt| match evt {
   |                   ------------------- value moved into closure here
10 |           enums::Event::Push => {
11 |               clicked(thisBtn, &mut btn2);
   |                                     ---- variable moved due to use in closure
...
18 | /     btn2.handle(move |thisBtn, evt| match evt {
19 | |         enums::Event::Push => {
20 | |             clicked(thisBtn, &mut btn1);
21 | |             true
22 | |         }
23 | |         _ => false,
24 | |     });
   | |______^ value borrowed here after move

For more information about this error, try `rustc --explain E0382`.

Okay, I understand that I move the borrowed btn2 so later I can't use it again. So I tried to remove move so btn2 stays in the current scope, but failed:

error[E0373]: closure may outlive the current function, but it borrows `btn2`, which is owned by the current function

Solution

  • This looks like the classic case for Rc<RefCell<T>>:

    let btn1 = Rc::new(RefCell::new(button::Button::new(100, 100, 100, 50, "BTN1")));
    let btn2 = Rc::new(RefCell::new(button::Button::new(300, 100, 150, 50, "BTN2")));
    let (btn1_clone, btn2_clone) = (Rc::clone(&btn1), Rc::clone(&btn2));
    btn1.borrow_mut().handle(move |this_btn, evt| match evt {
        enums::Event::Push => {
            clicked(this_btn, &mut *btn2_clone.borrow_mut());
            true
        }
        _ => false,
    });
    btn2.borrow_mut().handle(move |this_btn, evt| match evt {
        enums::Event::Push => {
            clicked(this_btn, &mut *btn1_clone.borrow_mut());
            true
        }
        _ => false,
    });