Cow în Rust

„Clone on write”: ține fie un împrumut, fie o valoare deținută; copiază datele doar dacă trebuie modificate.

Sintaxă
use std::borrow::Cow;

pub enum Cow<'a, B: ?Sized + ToOwned> {
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

Ce face Cow

Cow<str> este fie Cow::Borrowed(&str), fie Cow::Owned(String). Este util pentru funcții care de cele mai multe ori returnează textul primit neschimbat și doar uneori trebuie să creeze unul nou: în cazul obișnuit nu se alocă nimic.

Se folosește ca un &str obișnuit (prin Deref): îl poți afișa, compara, apela metode pe el. to_mut() îți dă acces de modificare, transformând împrumutul în valoare deținută doar la nevoie; into_owned() îți dă String-ul. String::from_utf8_lossy este un exemplu din std care returnează Cow<str>.

Parametri

Parametru Descriere
Borrowed(&amp;B) Varianta fără copiere.
Owned(B::Owned) Varianta cu valoare proprie (String pentru str, Vec<T> pentru [T]).

Ce returnează

Un Cow; into_owned() returnează valoarea deținută, to_mut() o referință mutabilă la ea.

Exemple

Exemplul 1: Corectarea diacriticelor doar când e nevoie

Textele corecte trec fără nicio copiere; doar cele cu s/t cu sedilă (U+015F, U+0163) produc un String nou.

use std::borrow::Cow;

fn corecteaza(text: &str) -> Cow<'_, str> {
    if text.contains(['\u{015F}', '\u{0163}']) {
        Cow::Owned(text.replace('\u{015F}', "ș").replace('\u{0163}', "ț"))
    } else {
        Cow::Borrowed(text)
    }
}

fn main() {
    let texte = ["Brașov", "Bra\u{015F}ov", "Galați", "Gala\u{0163}i"];
    for t in texte {
        let r = corecteaza(t);
        let tip = match r {
            Cow::Borrowed(_) => "împrumutat",
            Cow::Owned(_) => "copie nouă",
        };
        println!("{r} ({tip})");
    }
}

Rezultat

Brașov (împrumutat)
Brașov (copie nouă)
Galați (împrumutat)
Galați (copie nouă)

Exemplul 2: to_mut și into_owned

use std::borrow::Cow;

fn main() {
    let note = [7, 9, 10];
    let mut c: Cow<[i32]> = Cow::Borrowed(&note);
    println!("înainte: {:?}, împrumutat: {}", c, matches!(c, Cow::Borrowed(_)));

    c.to_mut().push(8);            // acum se face copia
    println!("după:    {:?}, împrumutat: {}", c, matches!(c, Cow::Borrowed(_)));

    let v: Vec<i32> = c.into_owned();
    println!("vector: {v:?}, originalul: {note:?}");
}

Rezultat

înainte: [7, 9, 10], împrumutat: true
după:    [7, 9, 10, 8], împrumutat: false
vector: [7, 9, 10, 8], originalul: [7, 9, 10]

Greșeli frecvente

  • Returnezi mereu Cow::Owned: pierzi singurul avantaj al lui Cow. Folosește Borrowed când datele nu se schimbă.
  • Uiți durata de viață în semnătură: fn f(s: &str) -> Cow<str> dă avertismentul hiding a lifetime that's elided elsewhere is confusing (eroare cu -D warnings); scrie Cow<'_, str>, ca să se vadă că rezultatul împrumută din parametru.

Comenzi înrudite

  • String::from_utf8: Creează un String din octeți: from_utf8 verifică UTF-8 și dă eroare; from_utf8_lossy înlocuiește octeții greșiți cu �.
  • str::to_string: Copiază un &str într-un String nou, deținut. to_string și to_owned dau același rezultat.
  • Clone: Trait-ul pentru copierea explicită a unei valori cu .clone(); se obține cu #[derive(Clone)] sau implementare manuală.
  • Deref: Trait-ul din spatele operatorului * și al conversiei automate &Box<T> -> &T, &String -> &str (deref coercion).