This pertains to the problem discussed in
Clone into closures#2407, which describes a problem of tedium that I frequently run into myself regarding the need to declare intermediate "clone" variables to pass to move closures:
let foo = Arc::new(...);
let foo_clone = foo.clone();
thread::spawn(move || {
foo_clone.baz();
});
foo.baz();
That discussion had me wondering whether perhaps this could be alleviated by adding a marker keyword for variables analogous to the mut marker, that would instruct to compiler to automatically call clone for us when it needs to, such as when moved into a closure or when passed to a function that takes ownership:
let autoclone foo = Foo::new(...); // Must implement `Clone`, otherwise error
thread::spawn(move || { // Clones `foo`
foo.baz();
});
takes_ownership(foo); // Clones `foo`
let bar = foo; // End of `foo`'s NLL-scope (it's never used again), no clone required: moves `foo`
The autoclone marker would be allowed in all the same places the mut marker is currently allowed. Like mut, it would apply only to the variable until the end of that variable's scope. It would not be inherited by downstream variables (e.g. bar in the example above is not marked autoclone, the compiler is not allowed to automatically clone bar). It would be allowed along-side the mut marker, e.g.:
let autoclone mut foo = Foo::new(..);
This does introduce more magic. Especially in longer function bodies, one might come across the use of a variable foo that is not obviously autoclone as its declaration happened way earlier in the code. I think this is alleviated by the fact that the effect of autoclone is always limited to a single lexical scope, that is to say, there is only ever one place someone would have to look to determine if a foo variable is autoclone, which is the declaration site for foo. I would also argue that variables that hold Copy values come with a similar (identical?) problem, which could be taken as precedent. One might say that the autoclone marker essentially tells the compiler: treat this variable as if it were Copy (if it isn't copy already), except you must explicitly call the Clone implementation.
Some further considerations:
- Perhaps disallow the
autoclonemarker for values that are alsoCopy, to prevent ambiguity in what happens on a move (e.g.let autoclone foo = Foo::new();errors ifFooimplementsCopy). - Perhaps an explicit ordering should be defined when
autocloneis used alongsidemut, e.g. "autoclonemust always occur first" (let autoclone mut foo = ...), or "mutmust always occur first" (let mut autoclone foo =...). - "autoclone" is perhaps a bit long for a keyword, maybe someone can come up with a shorter label that conveys a similar meaning.