lang (traits | typesystem | closures)
copy_closuresImplement Clone and Copy for closures where possible:
// Many closures can now be passed by-value to multiple functions:
fn call<F: FnOnce()>(f: F) { f() }
let hello = || println!("Hello, world!");
call(hello);
call(hello);
// Many `Iterator` combinators are now `Copy`/`Clone`:
let x = (1..100).map(|x| x * 5);
let _ = x.map(|x| x - 3); // moves `x` by `Copy`ing
let _ = x.chain(y); // moves `x` again
let _ = x.cycle(); // `.cycle()` is only possible when `Self: Clone`
// Closures which reference data mutably are not `Copy`/`Clone`:
let mut x = 0;
let incr_x = || x += 1;
call(incr_x);
call(incr_x); // ERROR: `incr_x` moved in the call above.
// `move` closures implement `Clone`/`Copy` if the values they capture
// implement `Clone`/`Copy`:
let mut x = 0;
let print_incr = move || { println!("{}", x); x += 1; };
fn call_three_times<F: FnMut()>(mut f: F) {
for i in 0..3 {
f();
}
}
call_three_times(print_incr); // prints "0", "1", "2"
call_three_times(print_incr); // prints "0", "1", "2"
Idiomatic Rust often includes liberal use of closures.
Many APIs have combinator functions which wrap closures to provide additional
functionality (e.g. methods in the Iterator and Future traits).
However, closures are unique, unnameable types which do not implement Copy
or Clone. This makes using closures unergonomic and limits their usability.
Functions which take closures, Iterator or Future combinators, or other
closure-based types by-value are impossible to call multiple times.
One current workaround is to use the coercion from non-capturing closures to
fn pointers, but this introduces unnecessary dynamic dispatch and prevents
closures from capturing values, even zero-sized ones.
This RFC solves this issue by implementing the Copy and Clone traits on
closures where possible.
If a non-move closure doesn't mutate captured variables,
then it is Copy and Clone:
let x = 5;
let print_x = || println!("{}", x); // `print_x` is `Copy + Clone`.
// No-op helper function which moves a value
fn move_it<T>(_: T) {}
// Because `print_x` is `Copy`, we can pass it by-value multiple times:
move_it(print_x);
move_it(print_x);
Non-move closures which mutate captured variables are neither Copy nor
Clone:
let mut x = 0;
// `incr` mutates `x` and isn't a `move` closure,
// so it's neither `Copy` nor `Clone`
let incr = || { x += 1; };
move_it(incr);
move_it(incr); // ERROR: `print_incr` moved in the call above
move closures are only Copy or Clone if the values they capture are
Copy or Clone:
let x = 5;
// `x` is `Copy + Clone`, so `print_x` is `Copy + Clone`:
let print_x = move || println!("{}", x);
let foo = String::from("foo");
// `foo` is `Clone` but not `Copy`, so `print_foo` is `Clone` but not `Copy`:
let print_foo = move || println!("{}", foo);
// Even closures which mutate variables are `Clone + Copy`
// if their captures are `Clone + Copy`:
let mut x = 0;
// `x` is `Clone + Copy`, so `print_incr` is `Clone + Copy`:
let print_incr = move || { println!("{}", x); x += 1; };
move_it(print_incr);
move_it(print_incr);
move_it(print_incr);
Closures are internally represented as structs which contain either values
or references to the values of captured variables
(move or non-move closures).
A closure type implements Clone or Copy if and only if the all values in
the closure's internal representation implement Clone or Copy:
Non-mutating non-move closures only contain immutable references
(which are Copy + Clone), so these closures are Copy + Clone.
Mutating non-move closures contain mutable references, which are neither
Copy nor Clone, so these closures are neither Copy nor Clone.
move closures contain values moved out of the enclosing scope, so these
closures are Clone or Copy if and only if all of the values they capture
are Clone or Copy.
The internal implementation of Clone for non-Copy closures will resemble
the basic implementation generated by derive, but the order in which values
are Cloned will remain unspecified.
This feature increases the complexity of the language, as it will force users
to reason about which variables are being captured in order to understand
whether or not a closure is Copy or Clone.
However, this can be mitigated through error messages which point to the
specific captured variables that prevent a closure from satisfying Copy or
Clone bounds.
It would be possible to implement Clone or Copy for a more minimal set of
closures, such as only non-move closures, or non-mutating closures.
This could make it easier to reason about exactly which closures implement
Copy or Clone, but this would come at the cost of greatly decreased
functionality.
Copy or Clone?