是否为Moved值调用了克隆?

时间:2015-04-06 23:47:43

标签: rust

我试图理解Copy如何与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);
}

这是我的困惑:如果X不是“复制”,那么doit将取得对象z的所有权并将其放在范围的末尾。然后,主要的第二个println会抱怨,因为z被移动了。精细。但是,现在我将X标记为Copy并提供了一个克隆方法。我期望克隆方法将用于提供自己的z副本,从而允许我在doit之后继续使用z。这不会发生。

我的理解在哪里错了?

2 个答案:

答案 0 :(得分:3)

Clone没什么特别的。它只是一个普通的图书馆特质。你可以自己定义它!

因此,.clone()仅在您明确调用它时使用。复制和移动都与Clone无关。当您致电doit(z)时,z被复制到Copy意义上,这意味着一个字节方式的副本。如果要克隆以将其传递给doit,请写下:

doit(z.clone());

答案 1 :(得分:0)

current (2021) documentation 现在更清晰了

<块引用>

复制和克隆有什么区别?

复制是隐式发生的,例如作为赋值 y = x 的一部分。 Copy 的行为不可重载;它总是一个简单的按位复制。

克隆是一个显式操作,x.clone()。 Clone 的实现可以提供安全复制值所需的任何特定于类型的行为。
例如Clone的{​​{1}}实现需要将指向的字符串缓冲区复制到堆中。
字符串值的简单按位复制只会复制指针,从而导致双倍释放。因此,String 是 Clone 而不是 Copy。

StringClone 的超特征,因此所有 Copy 也必须实现 Copy
如果一个类型是 Clone 那么它的 Copy 实现只需要返回 Clone

*self

另请参阅 2020 年的文章“Moves, copies and clones in Rust

<块引用>

当一个值被移动时,Rust 会做一个浅拷贝;但是如果你想像在 C++ 中一样创建一个深拷贝怎么办?
为此,类型必须首先实现 struct MyStruct; impl Copy for MyStruct { } impl Clone for MyStruct { fn clone(&self) -> MyStruct { *self } } trait。
然后要进行深层复制,客户端代码应调用 Clone 方法:

clone

这会在克隆调用后产生以下内存布局: https://hashrust.com/blog/moves-copies-and-clones-in-rust/vector-layout-cloned.svg 由于深度复制,let v: Vec<i32> = Vec::new(); let v1 = v.clone();//ok since Vec implements Clone println!("v's length is {}", v.len());//ok v 都可以自由地独立删除它们的堆缓冲区。

注意

v1 方法并不总是创建深层副本。

类型可以自由地以任何他们想要的方式实现克隆,但在语义上它应该足够接近复制对象的含义。
例如,RcArc 会增加引用计数。