Reputation: 153
I am trying to understand how Copy interacts with move semantics in Rust. I expected this program to clone the object, but it does not. I have rust 1.0.0-beta.
#[derive(Debug)]
struct X {
y : i32,
}
impl Clone for X {
fn clone(&self) -> X {
println!("clone");
X { y: 4 }
}
}
impl Copy for X { }
fn doit(x : X) {
println!("doit {:?}", x);
}
fn main() {
let z = X { y: 5 };
println!("main {:?}", z);
doit(z);
println!("main {:?}", z);
}
Here's my confusion: If X were not "Copy", doit would take ownership of the object z and drop it at the end of scope. Then, the second println in main would complain because z was moved. Fine. However, now I've marked X as Copy and provided a clone method. I expected the clone method would be used to provide doit with its own copy of z, thus allowing me to continue using z after doit. That doesn't happen.
Where is my understanding wrong?
Upvotes: 5
Views: 1206
Reputation: 1323483
The current (2021) documentation is now clearer
What's the difference between Copy and Clone?
Copies happen implicitly, for example as part of an assignment
y = x
. The behavior ofCopy
is not overloadable; it is always a simple bit-wise copy.Cloning is an explicit action,
x.clone()
. The implementation of Clone can provide any type-specific behavior necessary to duplicate values safely.
For example, the implementation ofClone
forString
needs to copy the pointed-to string buffer in the heap.
A simple bitwise copy of String values would merely copy the pointer, leading to a double free down the line. For this reason, String is Clone but not Copy.
Clone
is a supertrait ofCopy
, so everything which is Copy must also implementClone
.
If a type isCopy
then itsClone
implementation only needs to return*self
struct MyStruct;
impl Copy for MyStruct { }
impl Clone for MyStruct {
fn clone(&self) -> MyStruct {
*self
}
}
See also the 2020 article "Moves, copies and clones in Rust "
When a value is moved, Rust does a shallow copy; but what if you want to create a deep copy like in C++?
To allow that, a type must first implement theClone
trait.
Then to make a deep copy, client code should call theclone
method:let v: Vec<i32> = Vec::new(); let v1 = v.clone();//ok since Vec implements Clone println!("v's length is {}", v.len());//ok
This results in the following memory layout after the clone call: Due to deep copying, both
v
andv1
are free to independently drop their heap buffers.Note
The
clone
method doesn't always create a deep copy.Types are free to implement clone any way they want, but semantically it should be close enough to the meaning of duplicating an object.
For example,Rc
andArc
increment a reference count instead.
Upvotes: 2
Reputation:
Clone
is nothing special. It's just an ordinary library trait. You could define it yourself!
Consequently, .clone()
is only used when you explicitly call it. Neither copying nor moving has anything to do with Clone
. When you call doit(z)
, z
is copied in the Copy
sense, which means a byte-wise copy under the hood. If you want to clone to pass it to doit
, then write that:
doit(z.clone());
Upvotes: 3