Clone în Rust

Trait-ul pentru copierea explicită a unei valori cu .clone(); se obține cu #[derive(Clone)] sau implementare manuală.

Sintaxă
#[derive(Clone)]
struct Tip { ... }

pub trait Clone: Sized {
    fn clone(&self) -> Self;
}

Ce face Clone

În Rust, atribuirea let b = a; mută valoarea: după ea, a nu mai poate fi folosit (pentru tipuri ca String, Vec). Dacă ai nevoie de două valori independente, ceri explicit o copie cu a.clone(). Copia este completă („deep copy”): un Vec<String> clonat are propriile texte.

Cel mai simplu obții Clone cu #[derive(Clone)], care clonează câmp cu câmp; merge doar dacă toate câmpurile sunt Clone. Implementarea manuală (impl Clone for Tip, cu metoda clone) îți permite să schimbi ceva la copiere. Excepție de știut: Rc::clone și Arc::clone nu copiază datele, ci doar măresc numărul de proprietari.

clone() poate costa mult (alocă memorie), de aceea Rust nu o face niciodată automat. Înainte de a o adăuga ca să scapi de o eroare de împrumut, verifică dacă nu ajunge o referință (&a).

Parametri

Parametru Descriere
clone(&amp;self) Singura metodă obligatorie. Mai există clone_from(&mut self, sursa), care poate refolosi memoria destinației.

Ce returnează

clone() returnează o valoare nouă, de același tip, independentă de original.

Exemple

Exemplul 1: #[derive(Clone)] și mutarea

#[derive(Clone)]
struct Elev {
    nume: String,
    note: Vec<u8>,
}

fn main() {
    let ana = Elev { nume: String::from("Ana"), note: vec![9, 10] };
    let mut copie = ana.clone();      // copie completă
    copie.nume.push_str(" (copie)");
    copie.note.push(7);

    println!("{}: {:?}", ana.nume, ana.note);
    println!("{}: {:?}", copie.nume, copie.note);

    let mutat = ana;                  // fără clone: ana este mutată
    println!("mutat: {}", mutat.nume);
}

Rezultat

Ana: [9, 10]
Ana (copie): [9, 10, 7]
mutat: Ana

Exemplul 2: Implementare manuală

Copia unui document primește automat sufixul „(copie)” și o versiune resetată, lucru pe care derive nu l-ar face.

struct Document {
    titlu: String,
    versiune: u32,
}

impl Clone for Document {
    fn clone(&self) -> Self {
        Document {
            titlu: format!("{} (copie)", self.titlu),
            versiune: 1,
        }
    }
}

fn main() {
    let original = Document { titlu: String::from("Contract Iași"), versiune: 7 };
    let copie = original.clone();
    println!("{} v{}", original.titlu, original.versiune);
    println!("{} v{}", copie.titlu, copie.versiune);
}

Rezultat

Contract Iași v7
Contract Iași (copie) v1

Greșeli frecvente

  • Pui .clone() peste tot ca să treci de verificarea împrumuturilor. Codul compilează, dar copiază inutil; de multe ori o referință rezolvă problema.
  • Folosești #[derive(Clone)] pe o structură cu un câmp care nu e Clone (de exemplu un tip propriu fără derive): the trait `Clone` is not implemented. Adaugă derive(Clone) și pe acel tip.

Comenzi înrudite

  • Copy: Trait marker pentru tipurile copiate automat bit cu bit la atribuire, în loc să fie mutate (numere, char, bool…).
  • fmt::Debug: Trait-ul pentru afișarea de depanare cu {:?} și {:#?}; se obține aproape mereu cu #[derive(Debug)].
  • Default: Trait-ul pentru valoarea implicită a unui tip: Tip::default(), derive(Default) și ..Default::default().
  • Deref: Trait-ul din spatele operatorului * și al conversiei automate &Box<T> -> &T, &String -> &str (deref coercion).