Quelle est la différence entre Copy et Clone?


Réponses:


115

Cloneest conçu pour les duplications arbitraires: une Cloneimplémentation d'un type Tpeut effectuer des opérations arbitrairement compliquées nécessaires pour créer un nouveau T. C'est un trait normal (autre que d'être dans le prélude), et nécessite donc d'être utilisé comme un trait normal, avec des appels de méthode, etc.

Le Copytrait représente des valeurs qui peuvent être dupliquées en toute sécurité via memcpy: des choses comme les réaffectations et le passage d'un argument par valeur à une fonction sont toujours memcpys, et donc pour les Copytypes, le compilateur comprend qu'il n'a pas besoin de les considérer comme un mouvement .


5
Puis-je comprendre comme Cloneune copie profonde et une copie Copymiroir?
Djvu

11
Cloneouvre la possibilité que le type puisse faire une copie profonde ou superficielle: "arbitrairement compliqué".
poolie

85

La principale différence est que le clonage est explicite. La notation implicite signifie déplacer pour un non- Copytype.

// 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.

À propos, chaque Copytype doit également l'être Clone. Cependant, ils ne sont pas obligés de faire la même chose! Pour vos propres types, cela .clone()peut être une méthode arbitraire de votre choix, alors que la copie implicite déclenchera toujours a memcpy, pas l' clone(&self)implémentation.


1
Cool! Cela clarifie une question secondaire que j'avais concernant si le trait Clone fournit une copie implicite. Il s'avère que cette question et celle-ci étaient plus liées que je ne le pensais. Merci!
user12341234

Dans votre premier exemple, supposons que vous vouliez yobtenir un déplacement x, pas une copie, comme avec votre dernier exemple commenté w = v. Comment le préciseriez-vous?
johnbakers

2
Vous ne pouvez pas, et vous ne le faites pas, car il Copyest destiné à être implémenté pour des types "bon marché", comme u8dans l'exemple. Si vous écrivez un type assez lourd, pour lequel vous pensez qu'un mouvement est plus efficace qu'une copie, ne le faites pas impliquer Copy. Notez que dans le cas u8, vous ne pouvez pas être plus efficace avec un mouvement, car sous le capot, cela impliquerait probablement au moins une copie de pointeur - qui est déjà aussi chère qu'une copie u8, alors pourquoi s'embêter.
mdup

Cela signifie-t-il que la présence du Copytrait a un impact sur les portées implicites de durée de vie des variables? Si c'est le cas, je pense que c'est remarquable.
Brian Cain

7

Comme déjà couvert par d'autres réponses:

  • Copy est implicite, peu coûteux et ne peut pas être réimplémenté (memcpy).
  • Clone est explicite, peut être coûteux et peut être réimplémenté arbitrairement.

Ce qui manque parfois dans la discussion de Copyvs, Clonec'est que cela affecte également la façon dont le compilateur utilise les déplacements par rapport aux copies automatiques. Par exemple:

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

Le premier exemple ( PointCloneAndCopy) fonctionne bien ici à cause de la copie implicite, mais le deuxième exemple ( PointCloneOnly) ferait une erreur avec une utilisation après le déplacement:

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

Pour éviter le déplacement implicite, nous pourrions appeler explicitement let p2 = p1.clone();.

Cela peut soulever la question de savoir comment forcer un mouvement d'un type qui implémente le trait Copy? . Réponse courte: Vous ne pouvez pas / n'a pas de sens.


@Shepmaster Je l'ai supprimé bien que je le trouve beaucoup plus lisible car il contient le joli code couleur du compilateur Rust et je me suis spécifiquement assuré que tous les mots pertinents pour la recherche sont également contenus dans le texte.
bluenote10
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.