00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Variabili in Rust

Le variabili in Rust funzionano in modo diverso rispetto alla maggior parte degli altri linguaggi di programmazione. Per impostazione predefinita, tutte le variabili sono immutabili, una scelta progettuale che favorisce la sicurezza e la prevedibilitĂ  del codice. In questa guida esploreremo come dichiarare e utilizzare le variabili in Rust.

ImmutabilitĂ  per Default con let

In Rust, le variabili vengono dichiarate con la parola chiave let e sono immutabili per default:

fn main() {
    let x = 5;
    println!("Il valore di x è: {}", x);

    // x = 10; // Errore! Non è possibile assegnare un nuovo valore
               // a una variabile immutabile
}

Se si tenta di modificare una variabile immutabile, il compilatore genera un errore chiaro:

// error[E0384]: cannot assign twice to immutable variable `x`

Questa scelta progettuale incoraggia la scrittura di codice più sicuro, poiché il compilatore garantisce che i valori non cambino inaspettatamente.

MutabilitĂ  con let mut

Quando è necessario modificare il valore di una variabile, si usa let mut:

fn main() {
    let mut contatore = 0;
    println!("Contatore iniziale: {}", contatore);

    contatore = 1;
    println!("Contatore dopo incremento: {}", contatore);

    contatore += 1;
    println!("Contatore dopo secondo incremento: {}", contatore);
}

La parola chiave mut comunica esplicitamente l’intenzione di modificare la variabile, rendendo il codice più leggibile e le intenzioni del programmatore più chiare.

Variable Binding

In Rust, la dichiarazione di una variabile viene chiamata binding (legame). Il binding associa un nome a un valore:

fn main() {
    let nome = "Marco";           // Binding di una stringa
    let eta = 25;                 // Binding di un intero
    let attivo = true;            // Binding di un booleano

    println!("{} ha {} anni, attivo: {}", nome, eta, attivo);
}

Il binding in Rust implica anche il concetto di ownership (proprietĂ ): quando si fa il binding di un valore a una variabile, quella variabile diventa proprietaria del valore.

Annotazione del Tipo

Rust è un linguaggio con type inference (inferenza dei tipi): il compilatore è spesso in grado di dedurre il tipo di una variabile dal valore assegnato. Tuttavia, è possibile specificare esplicitamente il tipo:

fn main() {
    let x: i32 = 42;             // Intero a 32 bit con segno
    let pi: f64 = 3.14159;       // Numero in virgola mobile a 64 bit
    let attivo: bool = true;     // Booleano
    let lettera: char = 'A';     // Carattere Unicode

    println!("x = {}, pi = {}, attivo = {}, lettera = {}", x, pi, attivo, lettera);
}

L’annotazione del tipo è obbligatoria quando il compilatore non riesce a inferire il tipo, ad esempio con le costanti o in situazioni ambigue:

fn main() {
    // Senza annotazione, il compilatore non sa quale tipo numerico usare
    let indovinato: u32 = "42".parse().expect("Non è un numero!");
    println!("Valore: {}", indovinato);
}

Variabili Non Utilizzate con _

Se una variabile non viene utilizzata, il compilatore Rust genera un warning. Per sopprimere questo avviso, si può prefissare il nome con un underscore _:

fn main() {
    let _risultato_temporaneo = calcola_qualcosa();
    // Nessun warning anche se _risultato_temporaneo non viene usato

    let _ = 42; // Ignora completamente il valore
}

fn calcola_qualcosa() -> i32 {
    42
}

L’underscore singolo _ è un pattern speciale che non fa nessun binding: il valore viene scartato immediatamente. Invece, _nome crea comunque un binding, ma sopprime il warning.

Shadowing

Lo shadowing (oscuramento) permette di ridichiarare una variabile con lo stesso nome, creando di fatto una nuova variabile che “oscura” la precedente:

fn main() {
    let x = 5;
    println!("x = {}", x); // Stampa: x = 5

    let x = x + 1;
    println!("x = {}", x); // Stampa: x = 6

    let x = x * 2;
    println!("x = {}", x); // Stampa: x = 12
}

Lo shadowing funziona anche all’interno di blocchi di codice:

fn main() {
    let x = 10;

    {
        let x = x + 5; // Shadowing all'interno del blocco
        println!("x nel blocco interno: {}", x); // Stampa: 15
    }

    println!("x nel blocco esterno: {}", x); // Stampa: 10
}

Shadowing con Cambio di Tipo

Una caratteristica potente dello shadowing è che permette di cambiare il tipo della variabile mantenendo lo stesso nome:

fn main() {
    let spazi = "   ";           // tipo &str
    let spazi = spazi.len();     // tipo usize

    println!("Numero di spazi: {}", spazi); // Stampa: 3
}

Questo è particolarmente utile quando si vuole trasformare un valore senza dover inventare nomi diversi come spazi_str e spazi_num.

Differenza tra Shadowing e mut

Shadowing e mutabilitĂ  sono concetti distinti con comportamenti differenti:

fn main() {
    // Con mut: stessa variabile, stesso tipo, valore modificabile
    let mut a = 5;
    a = 10; // OK: stesso tipo (i32)
    // a = "ciao"; // Errore! Non si può cambiare il tipo

    // Con shadowing: nuova variabile, tipo può cambiare
    let b = 5;          // tipo i32
    let b = "ciao";     // tipo &str - OK con shadowing

    println!("a = {}, b = {}", a, b);
}

Le differenze principali sono:

  • mut permette di modificare il valore ma non il tipo della variabile.
  • Shadowing crea una nuova variabile con lo stesso nome, permettendo di cambiare sia il valore che il tipo.
  • Shadowing produce una variabile immutabile per default (a meno che non si usi anche let mut).
fn main() {
    // Shadowing è utile per trasformazioni sequenziali
    let input = "  42  ";
    let input = input.trim();        // &str senza spazi
    let input: i32 = input.parse().unwrap(); // i32

    println!("Valore finale: {}", input); // Stampa: 42
}

Conclusione

Il sistema di variabili di Rust è progettato per incoraggiare la scrittura di codice sicuro e prevedibile. L’immutabilità per default forza il programmatore a essere esplicito sulle proprie intenzioni, mentre lo shadowing offre flessibilità senza sacrificare la sicurezza. Comprendere la differenza tra mutabilità e shadowing è fondamentale per scrivere codice Rust idiomatico e sfruttare appieno le garanzie offerte dal compilatore.