slice-uri (&[T] și &str) în Rust

O „fereastră” spre o parte dintr-un array, Vec sau text: o referință care știe de unde începe și câte elemente are, fără să copieze nimic.

Sintaxă
&colectie[inceput..sfarsit]   // sfârșitul nu este inclus
&colectie[..3]   &colectie[2..]   &colectie[..]
fn f(date: &[i32])          // primește array, Vec sau o parte din ele
fn g(text: &str)            // primește String sau literal

Ce face slice-uri (&[T] și &str)

Un slice este o referință la o secvență continuă de elemente: &[T] pentru elemente de tip T și &str pentru text. El nu deține datele; ține doar adresa primului element și lungimea. Nu copiază nimic, deci este ieftin de creat și de transmis.

Se creează cu un interval: &v[1..4] conține elementele cu indicii 1, 2 și 3 (capătul din dreapta nu este inclus). &v[..2] înseamnă de la început, &v[2..] până la sfârșit, &v[..] totul. Un interval în afara datelor oprește programul cu panică.

Funcțiile care doar citesc o listă ar trebui să primească &[T], nu &Vec<T>: așa acceptă și array-uri, și vectori, și bucăți din ei. La fel, pentru text primește &str, nu &String. La un &str, capetele intervalului sunt în octeți și trebuie să cadă între caractere: &"Iași"[0..3] dă panică, pentru că ș ocupă octeții 2 și 3.

Exemple

Exemplul 1: Bucăți dintr-un vector

Aceeași funcție suma primește tot vectorul, o parte din el și un array. Niciun element nu este copiat.

fn suma(valori: &[i32]) -> i32 {
    valori.iter().sum()
}

fn main() {
    let vanzari = vec![120, 80, 95, 200, 150, 60];
    println!("Tot anul:       {}", suma(&vanzari));
    println!("Primele 3 luni: {}", suma(&vanzari[..3]));
    println!("Lunile 4-5:     {:?}", &vanzari[3..5]);
    println!("Un array:       {}", suma(&[1, 2, 3]));

    let (prima, a_doua) = vanzari.split_at(3);
    println!("{prima:?} | {a_doua:?}");
}

Rezultat

Tot anul:       705
Primele 3 luni: 295
Lunile 4-5:     [200, 150]
Un array:       6
[120, 80, 95] | [200, 150, 60]

Exemplul 2: Slice-uri de text (&str)

prenume se referă la o parte din nume_complet, fără să creeze un text nou. split_once returnează tot slice-uri. Pozițiile sunt în octeți; find returnează tot o poziție în octeți, deci intervalul cade corect între caractere.

fn initiala(text: &str) -> &str {
    let sfarsit = text.chars().next().map_or(0, |c| c.len_utf8());
    &text[..sfarsit]
}

fn main() {
    let nume_complet = String::from("Ștefan Popescu");
    let spatiu = nume_complet.find(' ').unwrap();
    let prenume = &nume_complet[..spatiu];
    println!("Prenume: {prenume} ({} octeți)", prenume.len());
    println!("Inițiala: {}", initiala(&nume_complet));

    if let Some((oras, judet)) = "Iași, IS".split_once(", ") {
        println!("Oraș: {oras}, județ: {judet}");
    }
}

Rezultat

Prenume: Ștefan (7 octeți)
Inițiala: Ș
Oraș: Iași, județ: IS

Exemplul 3: Interval care taie un caracter

Ș ocupă 2 octeți (0 și 1), deci octetul 1 este în mijlocul caracterului. Rust refuză să creeze un text invalid și oprește programul.

fn main() {
    let oras = "Ștei";
    println!("{}", &oras[0..2]);
    println!("{}", &oras[0..1]);
}

Rezultat (programul se oprește cu panică)

Ș

thread 'main' (14470836) panicked at main.rs:4:25:
end byte index 1 is not a char boundary; it is inside 'Ș' (bytes 0..2 of string)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Greșeli frecvente

  • Scrii funcții cu parametrul &Vec<i32> sau &String. Funcționează, dar nu acceptă array-uri sau bucăți; &[i32] și &str acceptă tot.
  • Crezi că &v[1..3] include indicele 3. Capătul din dreapta este exclus; pentru a-l include scrie &v[1..=3].
  • Tai un text cu poziții calculate ca număr de caractere. Pozițiile unui &str sunt în octeți; pentru primele N caractere folosește text.chars().take(n).collect::<String>() sau char_indices.

Comenzi înrudite

  • array ([T; N]): Un șir de lungime fixă, cunoscută la compilare, cu elemente de același tip, păstrat direct pe stivă.
  • borrowing și referințe (& și &mut): O referință (&) împrumută o valoare fără să o ia de la proprietar. Regula: oricâte referințe de citire SAU una singură de modificare.
  • ownership (proprietatea asupra valorilor): Regula de bază a lui Rust: fiecare valoare are un singur proprietar, iar când proprietarul dispare, valoarea este eliberată automat.
  • for … in: Parcurge pe rând fiecare element al unui interval, al unei colecții sau al unui iterator.