Rust 1.100: dopo 11 anni, qualcosa sta cambiando davvero

In questo articolo
Memoria modulare per singolo container, type system completo e sicurezza in Cargo: ecco cosa rende storica la release di Rust 1.100.
Cos'è Rust 1.100 e cosa introduce? Rust 1.100 è una milestone che consolida feature storiche proposte fin dal 2015 senza introdurre breaking changes. Introduce l'Allocator API per allocare memoria su singoli container anziché a livello globale, integra stabilmente il tipo never (!) nel type system, aggiunge la quarantena preventiva delle dipendenze in Cargo contro minacce alla supply chain, abilita il linting del manifest ed estende il compilatore con il Sample-Based PGO per ottimizzazioni guidate dal carico reale di produzione.
Allocator API. Il tipo Never !. Difese attive sulla supply-chain. Ottimizzazioni guidate da campionamento in produzione.
Rust 1.100 è pronto a sbarcare sul canale stable.
Mettiamo subito le cose in chiaro: non è Rust 2.0. E mai vorrebbe esserlo.
Eppure, questa versione rischia di diventare una delle milestone più simboliche dell'intera storia del linguaggio.
Il peso di undici anni di pazienza
11 anni.
Tanto è trascorso tra le prime discussioni dell'Allocator API e la sua approvazione definitiva in stable.
Perché proprio la release 1.100? Il numero tondo è pura aritmetica del calendario. Rust non fa "grandi lanci teatrali": pubblica ogni sei settimane, garantisce una retrocompatibilità granitica e promuove le feature da nightly a stable solo e soltanto quando hanno superato la prova del fuoco.
La particolarità di questa release non è il numero di versione, ma ciò che converge al suo interno:

Due tra le idee più ambiziose, nate fianco a fianco con Rust 1.0 nel 2015, tagliano finalmente il traguardo. Ed è qui che la storia si fa estremamente interessante.
La memoria non è più una decisione monolitica
Fino a ieri, cambiare allocatore in Rust era una scelta dicotomica: o tutto, o niente. Potevi rimpiazzare l'allocatore di sistema con un'alternativa performante come jemalloc o mimalloc tramite l'attributo globale:
#[global_allocator]
static GLOBAL: MyAllocator = MyAllocator;Ma la parola chiave è proprio quella: globale. Un'unica strategia di allocazione imposta a qualsiasi stringa, vettore o tabella hash dell'intera applicazione.
Con l'arrivo dell'**Allocator API**, questa limitazione scompare. Il modello architetturale si frammenta in componenti modulari:

L'allocatore entra nel Type System
L'allocatore non è più un dettaglio di implementazione invisibile a runtime: diventa parte integrante del tipo.
use std::alloc::{Allocator, System};
// Questa funzione non si limita a costruire un buffer:
// riceve il ciclo di vita della memoria direttamente dal chiamante.
fn build_frame_buffer(alloc: A) -> Vec {
Vec::with_capacity_in(4096, alloc)
}Tipi fondamentali come Vec e Box ora portano con sé la provenienza della loro memoria:
Generazione del diagramma…
Perché questo cambia le regole del gioco?
Se scrivi una comune API REST in Actix o Axum, probabilmente non noterai alcuna differenza.
Ma se costruisci:
* Game Engine: una Frame Arena che viene azzerata alla fine di ogni frame a costo quasi zero.
* Database & Storage Engines: un Buffer Pool dedicato per le pagine di memoria I/O, isolato dai metadati.
* Sistemi Real-time & Embedded: garanzia assoluta che certe strutture non tocchino mai l'heap di sistema, azzerando jitter e frammentazione.
* Librerie di terze parti: chi scrive codice per l'ecosistema non deve più presumere l'esistenza di un allocator di sistema:
> "Dimmi tu come e dove vuoi allocare la memoria, al resto penso io."
! diventa un vero cittadino del Type System
Un solo carattere, una potenza espressiva devastante:
!È il Never Type. Rappresenta un valore computazionale che non può esistere, un calcolo che non giunge mai a compimento o un ramo di esecuzione irraggiungibile:
fn supervisor_loop() -> ! {
loop {
poll_hardware_events();
}
}La vera magia avviene quando ! smette di essere un'eccezione del compilatore e comincia a interagire in modo puro con i tipi algebrici:
// Un'operazione che non può matematicamente fallire
type SafeResult = Result;Nel tipo Result, l'errore non è "vuoto" o "ignorato": è impossibile.
fn get_constant() -> Result {
Ok(42)
}
// Il branch err non necessita di gestione:
// il compilatore sa che non si verificherà mai.
let Ok(val) = get_constant(); Il tipo della standard library std::convert::Infallible trova la sua identità naturale, diventando un alias trasparente di !. È un tassello apparentemente minuscolo, ma dona una coerenza algebrica che pochi altri linguaggi possono vantare.
Cargo alza le barricate contro gli attacchi alla Supply Chain
La sicurezza non riguarda soltanto l'uso di puntatori validi o la gestione dei thread. La sicurezza moderna si combatte nell'ecosistema dei pacchetti. Rust 1.100 introduce una barriera di difesa essenziale in Cargo: la minimum publish age.
Nel tuo .cargo/config.toml:
[registry]
global-min-publish-age = "7 days"Cosa significa in concreto? Nessuna dipendenza appena pubblicata viene integrata automaticamente nel tuo albero di build prima che sia trascorso il periodo di cooldown specificato.
Generazione del diagramma…
Questa finestra temporale toglie ossigeno alle campagne di avvelenamento rapido della supply chain (typosquatting, account compromessi nottetempo, release malevole ritirate dopo poche ore). Non rende il codice inviolabile, ma introduce una preziosa zona cuscinetto per la difesa perimetrale.
Linting architetturale: Cargo valida se stesso
Finora la pipeline di analisi statica era chiara e stratificata:
rustc ──► Valida la correttezza del codice sorgente
Clippy ──► Valida l'idiomaticità e i bug logiciDa Rust 1.100, anche la struttura del manifest riceve verifiche di livello compiler-grade. Possiamo finalmente dichiarare lint per Cargo direttamente nel Cargo.toml:
[lints.cargo]
unused_dependencies = "deny"Aggiungi una dipendenza per un test veloce, te ne dimentichi, e finisce in produzione appesantendo i tempi di compilazione e la superficie d'attacco? Con questa riga, il build si interrompe istantaneamente.
Il tooling sta compiendo un salto di paradigma: non si chiede più solo "questo file compila?", ma "l'architettura del repository è sana e pulita?".
Il compilatore impara dai dati reali: Sample-Based PGO
Con Rust 1.100 debutta in stable il flag:
-C profile-sample-useFinora, il Profile-Guided Optimization (PGO) richiedeva due passaggi rigidi: compilare con strumentazione pesante inserita nel binario, eseguire workload artificiali per raccogliere i dati, e ricompilare da capo.
Con il Sample-Based PGO (AutoFDO), il compilatore sfrutta i dati generati da profiler a basso impatto a livello kernel (come Linux perf) registrati direttamente dai server di produzione sotto carico reale.
Generazione del diagramma…
Niente overhead di strumentazione, nessuna simulazione sintetica: il compilatore sa con certezza millimetrica quali rami di codice vengono sollecitati e organizza il binario per massimizzare la cache della CPU.
Perché Rust 1.100 è una release epocale
È facile riassumere questo aggiornamento con una lista della spesa: "abbiamo gli allocator custom e il never type".
Ma la vera riflessione va oltre le singole feature.
Nel 2015, la sfida di Rust era dimostrare una tesi audace:
È possibile fare systems programming moderno senza garbage collector e senza data race?
Nel 2026, la domanda è diventata un'altra:
Come trasformiamo quel nucleo in un'infrastruttura ad altissima precisione senza spezzare una sola riga di codice esistente?
2015 ──► Rust 1.0 : Ownership, Borrowing, Garanzie di Memoria
2018 ──► Rust 2018 : Non-Lexical Lifetimes, Nuovo Modulo Moduli
2019 ──► Rust 1.39 : Async / Await
2022 ──► Rust 1.65 : Generic Associated Types (GAT)
2026 ──► Rust 1.100: Allocator API, Type Completeness, Supply Chain FortificataRust 1.100 non introduce una sintassi stravagante. Non ti costringerà a riscrivere la tua base di codice. Non rompe nulla del passato.
Rust 1.100 sarà ricordato perché dimostra cosa significa maturità ingegneristica: preferire undici anni di paziente affinamento per consegnare un'API definitiva, piuttosto che affrettare una soluzione imperfetta solo per fare notizia.
Non c'è bisogno di una "Rust 2.0" quando le fondamenta della versione 1.0 sono state gettate con questa lungimiranza.
Punti chiave
- Memoria Granulare
- Type system algebrico completo
- Resilienza della supply chain
- Architettura del repository sotto verifica
- Sample-Based PGO in produzione
- Trionfo della stabilità
Domande frequenti
Rust 1.100 introduce breaking changes o richiede una nuova "Edition"?
No. Fedele al principio di stabilità di Rust garantito fin dalla versione 1.0, Rust 1.100 non rompe il codice esistente. Tutte le nuove capacità — dall'Allocator API al never type — sono estensioni additive pienamente compatibili a ritroso.
Qual è la differenza pratica tra GlobalAlloc e la nuova Allocator API?
GlobalAlloc impone una singola implementazione di allocatore a livello di intero processo. L'Allocator API permette invece di specificare l'allocatore come parametro di tipo sui singoli container (Vec, Box), consentendo a strutture dati critiche di convivere usando arene o memorie dedicate isolate dal resto del programma.
Come viene utilizzato il never type ! nel codice applicativo?
! denota un tipo computazionalmente disabitato (che non può produrre alcun valore). Consente di tipizzare funzioni divergenti (come loop infiniti o terminazioni di processo) e permette costrutti come Result per descrivere operazioni che matematicamente non possono fallire, eliminando rami di gestione inutili.
Cosa differenzia il Sample-Based PGO dal tradizionale PGO strumentato?
Il PGO classico richiede l'inserimento di contatori diagnostici nel binario (con impatto evidente su prestazioni e dimensioni) e l'esecuzione di scenari di test sintetici. Il Sample-Based PGO usa campionamenti a basso impatto generati da profiler hardware o kernel (come perf) su binari reali in produzione, offrendo al compilatore dati sul traffico effettivo.









