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
autoclone
marker for values that are alsoCopy
, to prevent ambiguity in what happens on a move (e.g.let autoclone foo = Foo::new();
errors ifFoo
implementsCopy
). - Perhaps an explicit ordering should be defined when
autoclone
is used alongsidemut
, e.g. "autoclone
must always occur first" (let autoclone mut foo = ...
), or "mut
must 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.