Really sorry about the basic question.
I am trying to simulate something like a web server call and capture a variable from an outer scope.
fn http_get(path: &str, f: &dyn Fn()) {
f();
}
fn main() {
let mut counter = 0;
http_get("/foo", &|| {
counter = counter + 1;
println!("/foo: {}", counter);
});
http_get("/bar", &|| {
counter = counter + 1;
println!("/bar: {}", counter);
});
}
This results in a compiler error cannot assign to 'counter', as it is a captured variable in a 'Fn' closure.
I know I am failing somewhere basic, any chance I could get a push in the right direction?
First, to explain why this does not work.
When you transfer your counter into the closure, you basically allow other code to use it at any time. If it's just one closure, you can move it into the closure, giving the ownership of the value to the inner function, and it will work fine, including mutations, because there is no way to violate borrow checker rules.
Now when you have two closures that must use the same value, and you want to have mutable references to the counter in both of them, borrow checker cannot know when this mutable references are dropped. The function, that uses your closure can save the closure for later. Then you would have two mutable reference to the same value at the same time, which is not allowed by the borrow checker.
There are several solutions to this situations.
RefCell. It provides inner mutability, so even you don't pass mutable reference to a closure, you still can mutate the value of counter. The borrow checker rules in this case a checked in runtime. If your code would try to get two mutable references at the same time, not dropping previous one, your code will panic.fn http_get(path: &str, f: &dyn Fn()) {
f();
}
fn main() {
let counter = 0;
let r = std::cell::RefCell::new(counter);
http_get("/foo", &|| {
let mut counter = r.borrow_mut();
*counter = *counter + 1;
println!("/foo: {}", counter);
});
http_get("/bar", &move || {
let mut counter = r.borrow_mut();
*counter = *counter + 1;
println!("/bar: {}", counter);
});
}
If you need to cross thread boundaries, you can use Arc and Mutex as @Zeppi answer suggests.
If you work with multiple threads and the value you need to mutate is as simple as counter, you should probably use Arc and atomics. They allow mutable access from multiple threads without locking.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With