我试图理解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。这不会发生。
我的理解在哪里错了?
答案 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。
String
是 Clone
的超特征,因此所有 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
这会在克隆调用后产生以下内存布局:
由于深度复制,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
方法并不总是创建深层副本。
类型可以自由地以任何他们想要的方式实现克隆,但在语义上它应该足够接近复制对象的含义。
例如,Rc
和 Arc
会增加引用计数。