variabile și mutabilitate (let, mut) în Rust

let creează o variabilă. Implicit nu poate fi modificată; cu let mut primește voie să-și schimbe valoarea.

Sintaxă
let nume = valoare;              // imuabilă, tipul este dedus
let nume: Tip = valoare;         // cu tip explicit
let mut nume = valoare;          // poate fi modificată
let (a, b) = (1, 2);             // desfacerea unui tuplu
let nume;                        // inițializată mai târziu

Ce face variabile și mutabilitate (let, mut)

let leagă o valoare de un nume. În Rust, variabilele sunt imuabile implicit: după let x = 5;, o atribuire x = 6; este eroare de compilare (E0384). Astfel vezi imediat, din declarație, care valori se schimbă în program.

Când ai nevoie să modifici valoarea, scrii let mut. Cuvântul mut apare și în referințe (&mut x), în parametri (mut v: Vec<i32>) și în tipare. Dacă pui mut la o variabilă pe care nu o modifici, compilatorul te avertizează.

Tipul se deduce de obicei din valoare; îl scrii când compilatorul nu are destule informații (let n: u32 = "7".parse().unwrap();). O variabilă poate fi declarată fără valoare și inițializată mai târziu, dar trebuie inițializată înainte de prima folosire.

Exemple

Exemplul 1: Variabilă imuabilă și variabilă mutabilă

nume rămâne la fel tot programul, deci nu are nevoie de mut. total se modifică în buclă, deci este declarat cu let mut.

fn main() {
    let nume = "Ana";
    let mut total = 0;
    for nota in [9, 10, 8] {
        total += nota;
    }
    println!("{nume} are în total {total} puncte");
}

Rezultat

Ana are în total 27 puncte

Exemplul 2: Ce se întâmplă dacă modifici o variabilă fără mut

Compilatorul refuză programul și spune exact ce să schimbi: let mut stoc = 10;.

fn main() {
    let stoc = 10;
    println!("Stoc inițial: {stoc}");
    stoc = stoc - 3;
    println!("Stoc după vânzare: {stoc}");
}

Mesajul compilatorului (programul nu se compilează)

error[E0384]: cannot assign twice to immutable variable `stoc`
 --> main.rs:4:5
  |
2 |     let stoc = 10;
  |         ---- first assignment to `stoc`
3 |     println!("Stoc inițial: {stoc}");
4 |     stoc = stoc - 3;
  |     ^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut stoc = 10;
  |         +++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0384`.

Exemplul 3: Tip explicit, desfacere și inițializare întârziată

parse() poate produce multe tipuri, deci tipul u32 trebuie scris. let (oras, judet) = ... desface un tuplu. mesaj primește valoare o singură dată, pe una dintre ramuri, deci nu are nevoie de mut.

fn main() {
    let locuitori: u32 = "271692".parse().unwrap();
    let (oras, judet) = ("Iași", "IS");
    let mesaj;
    if locuitori > 200_000 {
        mesaj = "oraș mare";
    } else {
        mesaj = "oraș mic";
    }
    println!("{oras} ({judet}): {locuitori} locuitori, {mesaj}");
}

Rezultat

Iași (IS): 271692 locuitori, oraș mare

Greșeli frecvente

  • Uiți mut și primești cannot assign twice to immutable variable (E0384). Adaugă mut doar acolo unde valoarea chiar se schimbă.
  • Pui mut „ca să fie”: compilatorul dă avertismentul variable does not need to be mutable, iar cu -D warnings (cum sunt compilate exemplele de pe acest site) build-ul se oprește.
  • Confunzi let mut cu shadowing: let x = x + 1; creează o variabilă nouă, nu o modifică pe cea veche. Vezi pagina despre shadowing.
  • Folosești o variabilă declarată cu let x; înainte de a-i da valoare: eroarea E0381 used binding isn't initialized.

Comenzi înrudite

  • shadowing (umbrirea variabilelor): Declari din nou o variabilă cu același nume: cea nouă o ascunde pe cea veche și poate avea chiar alt tip.
  • const și static: const definește o valoare fixă calculată la compilare; static definește o variabilă globală cu o singură adresă, care trăiește tot programul.
  • tipuri scalare (întregi, f64, bool, char): Tipurile de bază ale lui Rust: întregi cu și fără semn, numere cu virgulă, bool (true/false) și char (un caracter Unicode).
  • 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.
  • pattern matching (tipare): Tiparele descriu forma unei valori și scot din ea părțile de care ai nevoie: în let, match, if let, for și parametrii funcțiilor.