String în Rust

String Rust 1.0

Textul care îți aparține și se poate modifica: UTF-8, pe heap, crește singur. Diferența față de &str.

Sintaxă
let s: String = String::from("text");
let s = "text".to_string();
let felie: &str = &s;   // împrumut al textului

Ce face String

În Rust există două tipuri principale pentru text. String este textul pe care îl deții: stă pe heap, se poate modifica (push_str, insert, clear) și este eliberat automat când variabila iese din domeniu. &str (citește „string slice”) este doar o privire împrumutată spre un text care stă în altă parte: într-un String, în codul programului (literalii "Iași" au tipul &'static str) sau într-un fișier citit în memorie.

Regula practică: în structuri și când construiești text nou folosești String; ca parametru de funcție care doar citește textul folosești &str. Un &String se transformă automat în &str (prin Deref), deci funcția fn salut(nume: &str) primește și &oras, și "Cluj".

Textul este păstrat întotdeauna ca UTF-8. Literele obișnuite ocupă 1 octet, dar ă, â, î, ș, ț ocupă 2 octeți. De aceea len() numără octeți, nu litere, iar indexarea s[0] nu este permisă: compilatorul nu poate ști dacă poziția cade la mijlocul unei litere. Pentru litere folosești chars(), pentru bucăți folosești intervale de octeți &s[0..3] sau metoda sigură get.

Parametri

Parametru Descriere
String Text deținut, modificabil, pe heap. Se creează cu String::new(), String::from(...), to_string(), format!.
&str Referință (împrumut) la un text UTF-8 existent. Nu se poate modifica și nu deține memoria.

Ce returnează

Un obiect String. Metodele lui (push_str, len, chars etc.) au pagini separate; toate metodele lui &str se pot apela și pe un String.

Exemple

Exemplul 1: String și &str în aceeași funcție

Funcția primește &str, deci acceptă atât un literal, cât și un String împrumutat cu &.

fn salut(nume: &str) -> String {
    format!("Bună, {nume}!")
}

fn main() {
    let literal: &str = "Ana";                 // text din codul programului
    let mut deținut = String::from("Mihai");   // text care ne aparține
    deținut.push_str(" Popescu");              // îl putem modifica

    println!("{}", salut(literal));
    println!("{}", salut(&deținut));           // &String devine &str automat
    println!("{}", salut("Ioana"));
}

Rezultat

Bună, Ana!
Bună, Mihai Popescu!
Bună, Ioana!

Exemplul 2: Octeți, litere și diacritice

Numele are 6 litere, dar 7 octeți, pentru că Ș ocupă 2 octeți în UTF-8. chars().count() numără literele.

fn main() {
    let nume = String::from("Ștefan");

    println!("octeți (len):          {}", nume.len());
    println!("caractere (chars):     {}", nume.chars().count());
    println!("primul caracter:       {:?}", nume.chars().next());
    println!("al treilea caracter:   {:?}", nume.chars().nth(2));
    println!("primii 2 octeți:       {}", &nume[0..2]);   // exact litera Ș
}

Rezultat

octeți (len):          7
caractere (chars):     6
primul caracter:       Some('Ș')
al treilea caracter:   Some('e')
primii 2 octeți:       Ș

Exemplul 3: De ce nu merge oras[0]

Indexarea cu un singur număr nu compilează pentru texte. Mesajul real al compilatorului îți sugerează alternativele: .chars().nth() sau .bytes().nth().

fn main() {
    let oras = String::from("Iași");
    let prima = oras[0];
    println!("{prima}");
}

Eroarea de compilare (rustc)

error[E0277]: the type `str` cannot be indexed by `{integer}`
 --> main.rs:3:22
  |
3 |     let prima = oras[0];
  |                      ^ string indices are ranges of `usize`
  |
  = help: the trait `SliceIndex<str>` is not implemented for `{integer}`
  = note: you can use `.chars().nth()` or `.bytes().nth()`
  = note: required for `String` to implement `Index<{integer}>`

Greșeli frecvente

  • Scrii s[0] ca în alte limbaje. Rust nu permite: folosește s.chars().next() pentru prima literă sau s.as_bytes()[0] pentru primul octet.
  • Tai un text cu un interval care cade în mijlocul unei litere: &"Iași"[0..3] oprește programul cu panică (byte index 3 is not a char boundary; it is inside 'ș'). Folosește get(0..3), care returnează None în loc de panică.
  • Scrii fn f(s: &String). Merge, dar &str este mai flexibil: acceptă și literali, și bucăți de text.
  • Concatenezi cu s1 + s2 și te miri de eroare. Operatorul + cere în stânga un String (pe care îl consumă) și în dreapta un &str: s1 + &s2. De obicei format!("{s1}{s2}") este mai clar.

Comenzi înrudite

  • str: Tipul pentru o bucată de text UTF-8 împrumutată (&str): literalii, părți din String, parametrii de funcții.
  • String::from: Creează un String nou (deținut) din &str, char sau alt text, copiind conținutul pe heap.
  • String::len: Lungimea textului în OCTEȚI (nu în litere) și verificarea dacă textul e gol. Pentru litere: chars().count().
  • str::chars: Parcurge textul caracter cu caracter (char Unicode), nu octet cu octet. Cu count() afli numărul de litere.
  • char: Un singur caracter Unicode, scris între apostrofuri (‘ș’); ocupă mereu 4 octeți în memorie.
  • str::get: Taie textul sigur: get(a..b) returnează None în loc de panică; is_char_boundary verifică o poziție.