В чем разница между копированием и клонированием?

Ответы:

115

Cloneпредназначен для произвольного дублирования: Cloneреализация типа Tможет выполнять сколь угодно сложные операции, необходимые для создания нового T. Это обычная черта (кроме прелюдии), поэтому ее необходимо использовать как обычную черту, с вызовами методов и т. Д.

Эта Copyчерта представляет значения, которые можно безопасно дублировать с помощью memcpy: такие вещи, как переназначение и передача аргумента по значению функции, всегда memcpys, поэтому для 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()может быть произвольный метод по вашему выбору, тогда как неявное копирование всегда будет запускать a memcpy, а не clone(&self)реализацию.

mdup
источник
1
Прохладно! Это проясняет вторичный вопрос, который у меня был относительно того, обеспечивает ли свойство Clone неявное копирование. Оказалось, что этот вопрос и этот были более связаны, чем я думал. Спасибо!
user12341234
В вашем первом примере предположим, что вы хотите yполучить перемещенный объект x, а не его копию, как в последнем закомментированном примере w = v. Как бы вы это обозначили?
johnbakers
2
Вы не можете, и вы этого не сделаете, потому что Copyон предназначен для реализации для «дешевых» типов, как u8в примере. Если вы пишете довольно тяжелый шрифт, для которого, по вашему мнению, ход более эффективен, чем копия, делайте это не подразумеваемым Copy. Обратите внимание, что в случае с u8 вы не можете быть более эффективными с перемещением, так как под капотом это, вероятно, по крайней мере повлечет за собой копию указателя, которая уже так же дорога, как копия u8, так что зачем беспокоиться.
mdup
Означает ли это, что наличие Copyпризнака влияет на неявные области существования переменных? Если так, я думаю, это примечательно.
Брайан Кейн
7

Как уже упоминалось в других ответах:

  • Copy является неявным, недорогим и не может быть повторно реализован (memcpy).
  • Clone является явным, может быть дорогостоящим и может быть произвольно повторно реализован.

При обсуждении Copyvs иногда не хватает того, Cloneчто он также влияет на то, как компилятор использует ходы против автоматических копий. Например:

#[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(); .

Это может вызвать вопрос о том, как принудительно выполнить перемещение типа, который реализует свойство Copy? , Краткий ответ: вы не можете / не имеет смысла.

bluenote10
источник
@Shepmaster Я удалил его, хотя нахожу его более читабельным, потому что он содержит красивую цветовую кодировку компилятора Rust, и я специально удостоверился, что все релевантные для поиска слова также содержатся в тексте.
bluenote10