Rust entra in systemd. È il genere di notizia che invita a preparare due comunicati opposti: da una parte il funerale del C, dall’altra l’ennesima predica su quanto sia inutile cambiare linguaggio. Entrambi evitano il punto interessante. Perché in questa vicenda la decisione tecnicamente più importante non riguarda dove Rust può entrare, ma dove gli sviluppatori hanno deciso di non farlo entrare.
Il 24 ottobre 2026 è stata integrata nel ramo principale di systemd la pull request #44324, proposta da Christian Brauner. Abilita la scrittura di nuovi programmi periferici in Rust, mantenendo il core e le librerie condivise in C. Non è una riscrittura, non è la migrazione di servizi esistenti e non è la promessa che il prossimo avvio di Linux sarà improvvisamente immune ai bug. È una scelta di architettura con confini espliciti. Ed è proprio questo a renderla interessante anche per chi non ha alcuna intenzione di programmare in Rust.
Una porta aperta, non un trasloco
Systemd non è un’applicazione che si può riscrivere nel fine settimana e sostituire il lunedì mattina. È un insieme di componenti che partecipa all’avvio del sistema, alla gestione dei servizi e a molte operazioni che altri software danno per scontate. Una regressione in un’utilità isolata è un problema; una regressione in una libreria condivisa da decine di componenti può diventare una catena di problemi.
La modifica approvata consente Rust soltanto per nuovi leaf programs, cioè programmi terminali dell’architettura da cui non dipendono altri componenti. Il codice C esistente non viene convertito. La libreria condivisa rimane in C. Non è prudenza di facciata: è un modo per introdurre una tecnologia nuova senza imporre a tutto il sistema, nello stesso momento, nuovi vincoli di compilazione, collegamento e manutenzione.
La distinzione è anche temporale. L’infrastruttura per questi programmi è stata integrata, ma il primo componente pensato per sfruttarla, systemd-keyring-setup, al momento della verifica è ancora in una pull request separata e aperta. Il progetto guarda a systemd 263, ma un obiettivo di sviluppo non è una release già distribuita. Confondere il supporto appena aggiunto con un programma già disponibile sarebbe un modo rapido per trasformare un fatto tecnico in una notizia sbagliata.
Il perimetro conta più del linguaggio
Quando si discute di sostituire C con Rust, il confronto si riduce spesso a una tabella di vantaggi: sicurezza della memoria, gestione delle ownership, compilatore più severo. Sono proprietà importanti, ma non raccontano cosa succede a un progetto che esiste da anni, ha interfacce stabili e deve continuare a funzionare durante il cambiamento. Una decisione architetturale non si misura soltanto da ciò che rende possibile. Si misura anche da quante cose può rompere.
Partire dai componenti periferici permette di restringere il raggio delle regressioni e di verificare le nuove convenzioni senza spostare contemporaneamente tutti i confini del sistema. Questo non elimina il rischio: lo rende più identificabile. Se un programma Rust deve rispettare le stesse interfacce di un programma C, la discussione smette di essere una gara fra sintassi e diventa una domanda concreta: quale contratto deve continuare a valere, qualunque linguaggio si scelga?
È un principio che vale anche fuori da Linux. In un altro caso di riscrittura in Rust, quello di Mold 3.0, il problema centrale era conservare il comportamento atteso del linker. Qui la strada è diversa: non riscrivere il nucleo, ma costruire un punto d’ingresso controllato. Due strategie differenti, accomunate dalla stessa necessità di non rompere il contratto con chi usa il software.
Una migrazione diventa governabile quando sai indicare il punto esatto in cui può fermarsi. Se quel punto non esiste, il problema non è ancora scegliere il linguaggio.
Rust sì, Cargo no: la build non è un dettaglio
Il secondo aspetto interessante è la catena di compilazione. Systemd continua a usare Meson e non introduce Cargo per costruire i nuovi programmi. La configurazione Rust è opzionale, con -Drust=auto come impostazione predefinita. In sostanza, la presenza di un compilatore Rust può abilitare il percorso nuovo, ma l’architettura di build del progetto non viene riorganizzata intorno a un secondo sistema di gestione.
Anche le dipendenze sono deliberatamente ristrette. La proposta ammette i crate core, alloc e libc, usa no_std ed esclude crate esterni. Nella configurazione descritta dall’autore non viene usato un allocatore globale; per le collezioni sono previste astrazioni che restituiscono errori quando un’allocazione fallisce. Non significa che Rust non possa allocare memoria. Significa che le modalità con cui lo fa devono rispettare le convenzioni operative del progetto, anziché importare automaticamente tutte quelle abituali dell’ecosistema Rust.
È qui che si vede la differenza fra adottare un linguaggio e adottare senza discussione il suo intero ecosistema. Cargo è utile in moltissimi contesti, ma systemd ha scelto di mantenere un solo sistema di build e di controllare con precisione dipendenze e dimensioni. Non è una sentenza contro Cargo: è una decisione coerente con i vincoli di quel software. Un’azienda che aggiunge un nuovo stack senza chiedersi chi lo compilerà, chi lo aggiornerà e come entrerà nella distribuzione non sta facendo innovazione. Sta soltanto spostando il conto a chi verrà dopo.
L’ABI C è il contratto, non un fastidio da aggirare
I nuovi programmi Rust devono collegarsi alle librerie condivise esistenti tramite interfacce C. I binding vengono generati durante la build a partire dagli stessi header e dalle stesse opzioni usate per compilare il codice originale. Sopra questi binding c’è uno strato che riproduce le convenzioni di systemd: errori basati su errno, logging, gestione dell’ingresso nel programma e parsing degli argomenti.
La proposta include anche un controllo di parità: un programma di prova in Rust deve produrre lo stesso aiuto a riga di comando, interpretare le opzioni nello stesso modo e restituire lo stesso risultato per --introspect-cli del corrispondente programma C. Il test confronta gli output byte per byte. È un dettaglio meno spettacolare di un annuncio sulla memory safety, ma è quello che permette di capire se un nuovo componente sta davvero rispettando l’ambiente in cui entra.
Il confine C rimane comunque un confine da controllare. Rust può prevenire intere classi di errori nel codice sicuro, ma non corregge magicamente una specifica sbagliata, una race logica o un contratto FFI interpretato male. È un problema già affrontato sul piano tecnico parlando di Rust, ABI e regressioni del compilatore: la correttezza di un componente non nasce da un’etichetta sul linguaggio, ma dalla verifica delle sue interfacce e del comportamento effettivo.
Il costo c’è. Il punto è sapere dove pagarlo
Nella pull request viene riportato un confronto circoscritto: un piccolo eseguibile di prova Rust, dopo lo stripping e con ottimizzazioni LTO, pesa circa 32 KB contro 20 KB della controparte C. Non è un benchmark universale e non autorizza conclusioni sulle prestazioni di systemd. Mostra però che anche una scelta motivata dalla sicurezza introduce costi misurabili: dimensioni, toolchain, tempi di build, compatibilità delle distribuzioni.
La proposta richiede versioni minime degli strumenti, fra cui Rust 1.85, bindgen 0.71.1 e Meson 1.4.0 per il percorso interessato. L’autore segnala inoltre problemi in alcune build di distribuzione che abilitano automaticamente tutte le funzionalità senza predisporre le dipendenze Rust. È un esempio concreto di quanto sia ingenuo separare il codice dal processo che lo produce. Una modifica può essere corretta in un repository e creare comunque lavoro aggiuntivo per chi impacchetta, distribuisce e mantiene il software.
La scelta di renderla opzionale è quindi parte della soluzione, non un dettaglio secondario. Consente di introdurre un percorso nuovo senza trasformarlo immediatamente in un obbligo per tutti. Naturalmente questa libertà ha un prezzo: bisogna testare anche la configurazione senza Rust, verificare le differenze fra toolchain e impedire che la strada facoltativa diventi una dipendenza nascosta.
La lezione per chi progetta software, non solo per chi scrive C
Di fronte a una nuova tecnologia, la domanda più comoda è sempre la stessa: “Conviene adottarla?”. Quella utile è più scomoda: “In quale parte del sistema possiamo adottarla senza perdere il controllo del resto?”. Per rispondere servono confini, contratti, strumenti di verifica, una strategia di distribuzione e la disponibilità a rinunciare a qualche comodità del nuovo ecosistema.
Systemd non ha dimostrato che Rust sia la soluzione a tutti i problemi del software di sistema. Ha mostrato qualcosa di più concreto: si può creare spazio per un linguaggio nuovo senza fingere che il software esistente, le sue dipendenze e le persone che lo mantengono smettano di contare. È un approccio meno vendibile di una riscrittura totale. E probabilmente è proprio per questo che merita di essere studiato.
Domande frequenti
Systemd sta riscrivendo il core in Rust?
No. La pull request #44324 abilita Rust per nuovi programmi periferici. Il core, le librerie condivise e il codice C esistente non vengono convertiti.
Perché systemd usa Rust senza Cargo?
Il progetto mantiene Meson come sistema di build e limita i crate ammessi, per controllare dipendenze e compatibilità con la distribuzione. Rust è opzionale tramite -Drust=auto.
Quali componenti Rust sono già stati integrati?
Il supporto infrastrutturale per nuovi programmi Rust è stato integrato il 24 ottobre 2026. La prima utility proposta, systemd-keyring-setup, era ancora in una pull request separata e aperta al momento della verifica.
Rust rende automaticamente systemd più sicuro?
Rust riduce alcune classi di errori di memoria nel codice sicuro, ma non elimina errori logici, problemi alle interfacce C o regressioni di build. La sicurezza dipende anche dai contratti e dai test.
