Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I move a variable out of a closure?

Tags:

closures

rust

I have a simple GTK app and I need to select a folder and pass it's path to a variable outside of the closure.

let mut path = "".to_owned();

button_open.connect_clicked(move |_| {
    let file_chooser = gtk::FileChooserDialog::new(
        "Open File", None, gtk::FileChooserAction::SelectFolder,
        [("Open", gtk::ResponseType::Ok), ("Cancel", gtk::ResponseType::Cancel)]);
    if file_chooser.run() == gtk::ResponseType::Ok as i32 {
        let filename = file_chooser.get_current_folder().unwrap();
    }

    file_chooser.destroy();
});

How do I assign filename to path? If I just write

path = filename;

I get this error:

src\main.rs:46:13: 46:28 error: cannot assign to captured outer variable in an `Fn` closure
src\main.rs:46             path = filename;
like image 276
estw272 Avatar asked Jan 05 '16 11:01

estw272


People also ask

What is a closure in C++?

A closure is the combination of a function and the lexical environment within which that function was declared. ... Closures are useful because they let you associate some data (the lexical environment) with a function that operates on that data.

What is the difference between each privatecounter closure?

Each closure references a different version of the privateCounter variable through its own closure. Each time one of the counters is called, its lexical environment changes by changing the value of this variable. Changes to the variable value in one closure don't affect the value in the other closure.

What is a closure function in JavaScript?

Closures A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

What happens if a callback closes over a variable in a thread?

Then if the callback closes over the variables in the main thread, there may be problems. Most dire ones are mitigated by 'staticbound, but data races are still possible without synchronization which in gtk-rsis not enforced (no Sendbound on closures). – Vladimir Matveev Jan 7 '16 at 14:35 1 Thanks for your reply. If you don't mind, one question.


1 Answers

You can't do this for several reasons. Here is the definition of connect_clicked() method:

fn connect_clicked<F: Fn(Button) + 'static>(&self, f: F) -> u64

The closure which this method accepts is Fn and is bounded by 'static. This means, first, that it can't modify its environment (that would be FnMut) and it also can't capture anything by reference (roughly what 'static means). Therefore, there is no way for the closure to modify the path variable like you want.

Given that gtk-rs enforces that GTK can only be used from the main thread, and its widgets are not Sendable, there is no way for the handler to access variables from another thread, so I'm not sure why these closures have 'static bound, and I also see absolutely no reason why they are not FnMut. This seems to be an implementation defect.

Anyway, you can use Rc<RefCell<..>> to create a mutable piece of data:

let path: Rc<RefCell<Option<String>>> = Rc::new(RefCell::new(None));
let captured_path = path.clone();  // clone the Arc to use in closure
...
// inside the closure
*captured_path.borrow_mut() = Some(filename);

It is also possible to use Arc<Mutex<..>>, but I don't think it is necessary because gtk-rs protects against using GTK from any thread except the main one.

like image 125
Vladimir Matveev Avatar answered Sep 29 '22 19:09

Vladimir Matveev