复制和克隆有什么区别?


Answers:


115

Clone设计用于任意重复:Clone类型的实现T可以执行创建new所需的任意复杂操作T。它是常规特征(而不是前奏),因此需要像常规特征一样使用,并带有方法调用等。

Copy特征表示可以通过安全地重复值memcpy:像调动和价值传递的参数传递给函数总是事情memcpy,依次类推的Copy类型,编译明白它并不需要考虑的一个举动


5
我可以理解为Clone深层副本,而Copy影子副本是吗?
Djvu

11
Clone开启了该类型可能执行深拷贝或浅拷贝的可能性:“任意复杂”。
poolie

85

主要区别在于克隆是显式的。隐含符号表示移动非Copy类型。

// u8 implements Copy
let x: u8 = 123;
let y = x;
// x can still be used
println!("x={}, y={}", x, y);

// Vec<u8> implements Clone, but not Copy
let v: Vec<u8> = vec![1, 2, 3];
let w = v.clone();
//let w = v // This would *move* the value, rendering v unusable.

顺便说一句,每种Copy类型都必须是Clone。但是,他们不需要做同样的事情!对于您自己的类型,.clone()可以是您选择的任意方法,而隐式复制将始终触发memcpy,而不是clone(&self)实现。


1
凉!这清除了我关于克隆特征是否提供隐式复制的第二个问题。事实证明,这个问题比我想象的要相关得多。谢谢!
user12341234年

在第一个示例中,假设您想y移动x而不是复制它,就像上一个注释掉的示例一样w = v。您将如何指定呢?
johnbakers

2
您不能,也不能,因为Copy它是为“便宜”类型实现的,例如u8在示例中。如果您编写了一个重量级的类型,而您认为移动比复制有效,请不要隐式Copy。请注意,在u8情况下,移动可能无法提高效率,因为在幕后它可能至少需要一个指针副本-它已经与u8副本一样昂贵,所以何必麻烦一下。
mdup

这是否意味着Copy特征的存在对变量的隐式生存期范围有影响?如果是这样,我认为这是值得注意的。
Brian Cain

7

正如其他答案已经涵盖的那样:

  • Copy 是隐式的,廉价的,并且无法重新实现(memcpy)。
  • Clone 是明确的,可能会很昂贵,并且可能会任意重新实现。

Copyvs 讨论中有时会丢失的Clone是,它还影响编译器如何使用move vs自动副本。例如:

#[derive(Debug, Clone, Copy)]
pub struct PointCloneAndCopy {
    pub x: f64,
}

#[derive(Debug, Clone)]
pub struct PointCloneOnly {
    pub x: f64,
}

fn test_copy_and_clone() {
    let p1 = PointCloneAndCopy { x: 0. };
    let p2 = p1; // because type has `Copy`, it gets copied automatically.
    println!("{:?} {:?}", p1, p2);
}

fn test_clone_only() {
    let p1 = PointCloneOnly { x: 0. };
    let p2 = p1; // because type has no `Copy`, this is a move instead.
    println!("{:?} {:?}", p1, p2);
}

PointCloneAndCopy由于隐式副本,第一个示例()在这里工作正常,但是第二个示例(PointCloneOnly)在移动后使用会出错:

error[E0382]: borrow of moved value: `p1`
  --> src/lib.rs:20:27
   |
18 |     let p1 = PointCloneOnly { x: 0. };
   |         -- move occurs because `p1` has type `PointCloneOnly`, which does not implement the `Copy` trait
19 |     let p2 = p1;
   |              -- value moved here
20 |     println!("{:?} {:?}", p1, p2);
   |                           ^^ value borrowed here after move

为了避免隐式移动,我们可以显式调用let p2 = p1.clone();

这可能引发一个问题,即如何强制执行实现复制特征的类型的移动?。简短的回答:您不可能/没有道理。


@Shepmaster我删除了它,尽管我发现它更具可读性,因为它包含Rust编译器的漂亮颜色编码,并且我特别确保所有与搜索相关的词也都包含在文本中。
bluenote10
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.