Tags: rust, systems-programming, memory-safety, cargo, ownership
Level: intermediate
TL;DR: Rust is a highly performant systems programming language that guarantees memory safety without a garbage collector through its strict ownership and borrowing model.
Ownership: Every value has a single owner, and memory is automatically freed when the owner goes out of scope.
Borrowing: References allow reading (&T) or modifying (&mut T) data without transferring ownership.
Lifetimes: Compile-time annotations that ensure all references remain valid and prevent dangling pointers.
Traits: Interfaces that define shared behavior for types and enable static or dynamic dispatch.
Concurrency: Fearless concurrency is achieved by enforcing thread safety and preventing data races at compile time.
Error Handling: Relies on Result<T, E> for recoverable errors and Option<T> for the absence of values, avoiding exceptions.
Cargo: The standard build system and package manager for dependency management and testing.
Pattern Matching: match expressions force exhaustive handling of all possible data variants.
Smart Pointers: Types like Box, Rc, and Arc handle heap allocation, reference counting, and shared ownership.
Zero-cost abstractions: High-level constructs like iterators compile down to heavily optimized machine code.
Q: What is the difference between String and &str?R: I usually explain that String is a heap-allocated, owned, and mutable data structure, while &str is a lightweight, immutable borrowed reference to a string slice.
Q: How does Rust prevent data races?R: I point out that the compiler enforces a strict rule: you can have either one mutable reference or multiple immutable references to data at a time, but never both across threads.
Fighting the borrow checker instead of rethinking data structures and ownership boundaries.
Attempting to hold both mutable and immutable references to the exact# Rust — Cheat Sheet
Tag: rust, memory-management, borrow-checker, ownership, systems-programming
Livello: intermedio
TL;DR: Rust è un linguaggio di programmazione di sistema che garantisce sicurezza della memoria e thread safety attraverso il suo sistema di ownership e il borrow checker, senza l'uso di un garbage collector.
Ownership: Ogni valore in Rust ha un "proprietario" (owner). C'è un solo proprietario alla volta. Quando il proprietario esce dallo scope, il valore viene deallocato.
Borrowing (Prestiti): Invece di trasferire l'ownership, puoi passare i dati per riferimento (reference). I prestiti possono essere mutabili (&mut T) o immutabili (&T).
Borrow Checker: Il compilatore verifica le regole di ownership e borrowing a tempo di compilazione, prevenendo data races e problemi di memoria (es. dangling pointers).
Regole di Borrowing: Puoi avere *uno o più* riferimenti immutabili, OPPURE *esattamente un* riferimento mutabile alla volta.
Lifetimes: Annotazioni a tempo di compilazione che indicano per quanto tempo un riferimento è valido, per garantire che non sopravviva al dato a cui punta.
Mutabilità: Le variabili sono immutabili per impostazione predefinita. Devi usare mut per renderle mutabili.
Tipi Option ed Result: Usati pesantemente per la gestione degli errori e l'assenza di valori invece di eccezioni o null pointers.
Zero-cost Abstractions: Caratteristiche di alto livello che compilano in codice macchina efficiente come se l'avessi scritto in C a basso livello.
Q: Cos'è il borrow checker e perché è importante?R: È la parte del compilatore Rust che applica le regole di ownership. Garantisce la sicurezza della memoria senza un garbage collector, prevenendo dangling pointers, memory leaks e data races in modo statico (a tempo di compilazione).
Q: Qual è la differenza tra String e &str?R: String è una stringa allocata nell'heap, mutabile e di cui possiedi l'ownership (owned data). &str (string slice) è un riferimento immutabile a una sequenza di caratteri UTF-8, spesso puntando a dati statici nel binario o a un sottoinsieme di una String.
Q: Come gestisci gli errori in Rust?R: Principalmente con il tipo Result<T, E>. Per gli errori recuperabili uso Result (spesso con l'operatore ? per la propagazione), mentre per errori fatali e irrecuperabili si usa il macro panic!(). Non ci sono le classiche eccezioni try/catch.
Combattere il borrow checker cercando di applicare pattern orientati agli oggetti tradizionali (es. riferimenti circolari), che in Rust richiedono tipicamente costrutti speciali (come Rc, RefCell o Arc).
Tentare di mutare una variabile che non è stata dichiarata con mut.
Restituire riferimenti locali da una funzione. Dato che la variabile locale viene distrutta a fine funzione, il riferimento sarebbe "dangling". Il compilatore lo blocca, richiedendo di restituire l'ownership o usare i lifetime (se il dato proviene dai parametri).
Confondersi tra copia (Copy trait) e spostamento (Move semantics). I tipi base (interi, booleani) si copiano, i tipi complessi (come String) si muovono (passaggio di ownership) di default.
// Ownership e borrowing base
fn calculate_length(s: &String) -> usize { // S presta il valore a calculate_length (borrow)
s.len()
} // Qui s non viene deallocato perché non ne abbiamo l'ownership
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // Passiamo un riferimento immutabile
println!("La lunghezza di '{}' è {}.", s1, len);
// Mutabilità
let mut x = 5;
x = 6;
}
// Gestione Errori con Option e Result
fn get_element(vec: &Vec<i32>, index: usize) -> Option<i32> {
if index < vec.len() {
Some(vec[index])
} else {
None
}
}
[ ] Implementazione e utilizzo esplicito dei Lifetimes ('a).
[ ] Pattern matching avanzato con match.
[ ] Concorrenza: uso di Arc e Mutex per condividere stato tra i thread.
[ ] Differenza tra Box, Rc e RefCell (Smart Pointers).
The Rust Programming Language (The Book)
Rust by Example