Reputation: 1086
Is there a way to avoid creating temporary variables a variable is captured in multiple closures:
move
keyword in the closures is requiredCopy
trait but does not work as the inner value is a struct
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct MyStruct { value: u32 }
fn main() {
let shared = Rc::new(RefCell::new(MyStruct { value: 42}));
let shared_clone_1 = shared.clone(); // clone 1
let shared_clone_2 = shared.clone(); // clone 2
let shared_clone_3 = shared.clone(); // clone 3
let shared_clone_4 = shared.clone(); // clone 4: can these clones be hidden?
let closure_1 = move || { println!("closure_1 {:?}", shared); };
let closure_2 = move || { println!("closure_2 {:?}", shared_clone_1); };
let closure_3 = move || { println!("closure_3 {:?}", shared_clone_2); };
let closure_4 = move || { println!("closure_4 {:?}", shared_clone_3); };
let closure_5 = move || { println!("closure_5 {:?}", shared_clone_4); };
closure_1();
closure_2();
closure_3();
closure_4();
closure_5();
}
Upvotes: 0
Views: 55
Reputation: 42502
Is there a way to avoid creating temporary variables a variable is captured in multiple closures:
Not really? If you want to perform "precise" closure captures, then you need to define them.
Rust does not have built-in capture clauses (à la C++, let alone with support user-defined capture methods), but a common scheme is the so-called precise closure pattern where you define closed-over bindings in a block which also defines (and returns) the closure, and shadow the outer variables they're substituted for[0]:
let value = Rc::new(RefCell::new(MyStruct { value: 42}));
let closure_1 = {
let value = value.clone();
move || { println!("closure_1 {:?}", value); }
};
let closure_2 = {
let value = value.clone();
move || { println!("closure_2 {:?}", value); }
};
closure_1();
closure_2();
This makes the capture policies easier to see, and avoids duplicate names as you can shadow the outer name with the inner one.
The
move
keyword in the closures is required
A move
closure really just means all the captures are by value, whereas by default the compiler will try to use the least restrictive scheme (between shared reference, unique reference, and copy/move)[1].
So you can create a reference outside the closure and move the reference in the closure:
let value = MyStruct { value: 42};
let closure_1 = {
let value = &value;
move || { println!("closure_1 {:?}", value); }
};
let closure_2 = {
let value = &value;
move || { println!("closure_2 {:?}", value); }
};
closure_1();
closure_2();
It's fine to clone the container, I tried the Copy trait but does not work as the inner value is a struct
Unclear what you mean, you need to derive(Copy, Clone)
on your structure, although it requires that everything that contains is itself Copy
:
#[derive(Copy, Clone, Debug)]
struct MyStruct { value: u32 }
fn main() {
let value = MyStruct { value: 42};
let closure_1 = move || { println!("closure_1 {:?}", value); };
let closure_2 = move || { println!("closure_2 {:?}", value); };
closure_1();
closure_2();
}
[0]: and you could always build a precise capture macro over that e.g. https://crates.io/crates/capture
[1]: however this is based exclusively on the way the name is used inside the closure, so it often fails due to lifetime issues with escaping closures, it does usually work for non-escaping synchronous closures (e.g. callbacks in non-escaping iterator chains) which is likely why it's the default
Upvotes: 3