rustclosuresownershiptui

Referencing a cell in multiple closures with Cursive


I am using the Cursive TUI crate and I am having trouble with a dialog button because I want to set an input to change with the dialog response. This is my code:

fn prompt_repeat(siv: &mut Cursive) -> bool {
    let input = Cell::new(false);

    siv.add_layer(views::Dialog::text("Do you want to retype the note again?")
        .title("Redo?")
        .button("Yes", |s| {
                input.set(true);
                s.quit();
            })
        .button("No", |s| {
                s.quit();
            }));

    siv.run();

    input.get()
}

This causes error E0373:

error[E0373]: closure may outlive the current function, but it borrows `input`, which is owned by the current function
  --> src/main.rs:94:23
   |
94 |         .button("No", |s| {
   |                       ^^^ may outlive borrowed value `input`
95 |                 input.set(false)
   |                 ----- `input` is borrowed here
   |
note: function requires argument type to outlive `'static`
  --> src/main.rs:89:19
   |
89 |       siv.add_layer(views::Dialog::text("Do you want to retype the note again?")
   |  ___________________^
90 | |         .title("Redo?")
91 | |         .button("Yes", |s| {
92 | |                 input.set(true)
...  |
95 | |                 input.set(false)
96 | |             })
   | |______________^
help: to force the closure to take ownership of `input` (and any other referenced variables), use the `move` keyword
   |
94 |         .button("No", move |s| {
   |                       ++++

I understand that this is caused because the compiler cannot know if input will still exist before the closure uses it, but I cannot use move because I need to reference the cell again to return it (I have tried this and the compiler tells me I have borrowed the value multiple times, which is correct).

How should I go about referencing the cell in multiple closures?


Solution

  • I agree with @Jmb, I'd probably go with a Rc reference counting smart pointer:

    fn prompt_repeat(siv: &mut Cursive) -> bool {
        let input = Rc::new(Cell::new(false));
        let input_2 = input.clone();
    
        siv.add_layer(
            views::Dialog::text("Do you want to retype the note again?")
                .title("Redo?")
                .button("Yes", move |s| {
                    input_2.set(true);
                    s.quit();
                })
                .button("No", move |s| {
                    s.quit();
                }),
        );
    
        siv.run();
    
        input.get()
    }