terpstra
terpstra

Reputation: 153

Clone not invoked for Moved value?

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

Answers (2)

VonC
VonC

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 of Copy 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 of Clone for String 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 of Copy, so everything which is Copy must also implement Clone.
If a type is Copy then its Clone 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 the Clone trait.
Then to make a deep copy, client code should call the clone 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: https://hashrust.com/blog/moves-copies-and-clones-in-rust/vector-layout-cloned.svg Due to deep copying, both v and v1 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 and Arc increment a reference count instead.

Upvotes: 2

user395760
user395760

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

Related Questions