Qual è la differenza tra Copia e Clona?


Risposte:


115

Cloneè progettato per duplicazioni arbitrarie: Cloneun'implementazione per un tipo Tpuò eseguire operazioni arbitrariamente complicate necessarie per crearne uno nuovo T. È un tratto normale (oltre a essere nel preludio), quindi richiede di essere utilizzato come un tratto normale, con chiamate di metodo, ecc.

Il Copytratto rappresenta valori che possono essere duplicati in modo sicuro tramite memcpy: cose come riassegnazioni e passaggio di un argomento per valore a una funzione sono sempre memcpys, quindi per i Copytipi, il compilatore capisce che non è necessario considerarli una mossa .


5
Posso capire com'è Cloneuna copia profonda e una copia Copyreplicata?
Djvu

11
Cloneapre la possibilità che il tipo possa fare una copia profonda o superficiale: "arbitrariamente complicata".
poolie

85

La differenza principale è che la clonazione è esplicita. La notazione implicita significa mossa per un non- Copytipo.

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

A proposito, anche ogni Copytipo deve essere Clone. Tuttavia, non sono tenuti a fare la stessa cosa! Per i tuoi tipi, .clone()può essere un metodo arbitrario di tua scelta, mentre la copia implicita attiverà sempre a memcpy, non l' clone(&self)implementazione.


1
Freddo! Questo chiarisce una domanda secondaria che avevo riguardo al fatto che il tratto Clone fornisca la copia implicita. È venuto fuori che la domanda e questa erano più correlate di quanto pensassi. Grazie!
user12341234

Nel tuo primo esempio, supponi di voler yottenere uno spostamento x, non una copia, come nel tuo ultimo esempio commentato w = v. Come lo specificheresti?
johnbakers

2
Non puoi, e non lo fai, perché Copyè pensato per essere implementato per tipi "economici", come u8nell'esempio. Se scrivi un tipo piuttosto pesante, per il quale pensi che una mossa sia più efficiente di una copia, non farlo impl Copy. Nota che nel caso di u8, non puoi essere più efficiente con una mossa, poiché sotto il cofano probabilmente comporterebbe almeno una copia del puntatore - che è già costosa come una copia di u8, quindi perché preoccuparsi.
mdup

Ciò significa che la presenza del Copytratto ha un impatto sugli ambiti di vita impliciti delle variabili? Se è così, penso che sia degno di nota.
Brian Cain

7

Come già coperto da altre risposte:

  • Copy è implicito, poco costoso e non può essere reimplementato (memcpy).
  • Clone è esplicito, può essere costoso e può essere reimplementato arbitrariamente.

Ciò che a volte manca nella discussione di Copyvs Cloneè che influisce anche sul modo in cui il compilatore utilizza le mosse rispetto alle copie automatiche. Per esempio:

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

Il primo example ( PointCloneAndCopy) funziona bene qui a causa della copia implicita, ma il secondo example ( PointCloneOnly) avrebbe un errore con un utilizzo dopo lo spostamento:

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

Per evitare la mossa implicita, potremmo chiamare esplicitamente let p2 = p1.clone();.

Ciò potrebbe sollevare la questione di come forzare una mossa di un tipo che implementa il tratto Copia? . Risposta breve: non puoi / non ha senso.


@Shepmaster l'ho rimosso anche se lo trovo molto più leggibile perché contiene la bella codifica a colori del compilatore Rust e mi sono assicurato specificatamente che tutte le parole rilevanti per la ricerca fossero contenute anche nel testo.
bluenote10
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.