Rust o Go: come scegliere senza farsi guidare dai benchmark
Rust o Go? La differenza vera non è la velocità ma cosa paghi e quando: tempo di sviluppo contro garanzie a runtime. Criteri concreti per scegliere.
Cerchi "Rust vs Go" e trovi soprattutto grafici: richieste al secondo, millisecondi, memoria occupata. Rust vince quasi sempre, e la conclusione sembra ovvia. Poi scopri che Docker, Kubernetes e Terraform sono scritti in Go, e i conti non tornano più. In questo articolo trovi la differenza che i benchmark non mostrano: cosa paghi con ciascuno dei due, e in quale momento lo paghi.
La risposta breve
Go ti fa pagare poco all'inizio e qualcosa a runtime; Rust ti fa pagare molto all'inizio e quasi niente a runtime. Scegli Go quando il costo che conta è il tempo delle persone, Rust quando il costo che conta è un errore o una pausa in produzione.
Tutto il resto dell'articolo è un'espansione di questa frase. Se ti serve prima capire i due linguaggi singolarmente, li trovi in cos'è Go e cos'è Rust: qui non li rispiego, li metto uno contro l'altro dove la scelta si decide davvero.
Perché "quale è più veloce" è la domanda sbagliata
Rust è più veloce di Go nella maggior parte dei benchmark. È vero ed è quasi sempre irrilevante.
Un servizio web tipico passa la maggior parte del suo tempo ad aspettare: il database, un'altra API, il disco, la rete. Se una richiesta impiega cento millisecondi e novanta sono attesa della query, riscrivere il servizio in un linguaggio due volte più veloce ti fa guadagnare cinque millisecondi. Nessun utente se ne accorgerà.
I benchmark misurano il calcolo puro, cioè esattamente la parte che nel tuo servizio pesa meno. Per questo in molti progetti la differenza di prestazioni tra i due si vede solo sul grafico, non sulla fattura né sull'esperienza di chi usa il prodotto.
Dove la velocità conta davvero è un'altra categoria di software: motori di database, codec, compressione, motori di gioco, elaborazione di grandi volumi in memoria, componenti che girano milioni di volte al secondo. Lì Rust ha un vantaggio concreto — ma se stai costruendo quel tipo di software, probabilmente lo sai già.
La differenza vera: il garbage collector
Il punto tecnico che separa i due non è la velocità media, è la prevedibilità.
Go ha un garbage collector: il runtime, periodicamente, cerca la memoria non più usata e la libera. Il collector di Go è molto buono e le sue pause sono brevi, ma esistono. Rust non ce l'ha: la memoria viene liberata nel punto esatto in cui il compilatore sa che non serve più, grazie al sistema di ownership.
Nella vita reale questo si traduce così:
- Sulla latenza media i due sono spesso vicini.
- Sulla latenza di coda — la richiesta più lenta su mille, o su centomila — Rust è più stabile, perché non c'è nessun momento in cui il programma si ferma per fare pulizia.
- Sulla memoria occupata Rust tende a usarne meno, perché un garbage collector ha bisogno di margine per lavorare.
Per un'API aziendale queste differenze non cambiano niente. Per un sistema di trading, un motore che elabora audio in tempo reale o un componente embedded con poca RAM, cambiano tutto.
Il costo che non compare nei benchmark: il tempo delle persone
Questa è la metà della bilancia che i confronti ignorano, e spesso è la più pesante.
Go si impara in giorni. Un programmatore che conosce un qualsiasi altro linguaggio è produttivo in una settimana, e legge il codice dei colleghi senza fatica perché il linguaggio lascia pochissime scelte stilistiche.
Rust si impara in mesi. Non perché sia confuso, ma perché ti chiede di ragionare esplicitamente su chi possiede ogni dato e per quanto tempo. I primi mesi passano in buona parte a discutere col compilatore, e questa è un'esperienza raccontata da quasi tutti, non un problema di chi non si impegna.
A questo si somma il tempo di compilazione: Go compila quasi istantaneamente, Rust in modo sensibilmente più lento sui progetti grandi. Sembra un dettaglio finché non moltiplichi l'attesa per cinquanta compilazioni al giorno per dieci persone.
Il conto per un team: se devi assumere, formare, far ruotare persone su un progetto, Go abbassa il costo di ogni singolo passaggio. Rust lo alza — e in cambio ti dà un codice in cui intere categorie di bug non possono esistere.
Gestione degli errori: due filosofie opposte
Entrambi rifiutano le eccezioni, ma arrivano in posti diversi.
In Go l'errore è un valore che restituisci e che controlli a mano:
file, err := os.Open("config.json")
if err != nil {
return fmt.Errorf("apertura config: %w", err)
}
defer file.Close()
È verboso, ed è la critica più frequente al linguaggio. Ma il problema più serio non è la verbosità: il compilatore non ti obbliga a controllare err. Puoi ignorarlo e il programma compila.
In Rust l'errore è parte del tipo di ritorno, e il compilatore non ti lascia usare il risultato senza aver deciso cosa fare dell'errore:
fn leggi_config() -> Result<String, std::io::Error> {
let contenuto = std::fs::read_to_string("config.json")?;
Ok(contenuto)
}
L'operatore ? propaga l'errore in un carattere. È più conciso e più sicuro, ed è uno dei punti in cui Rust vince in modo netto, non solo per gusto.
Concorrenza: dove Go è davvero più semplice
Qui il confronto si ribalta.
In Go la concorrenza è il caso d'uso per cui il linguaggio è nato. Scrivi go funzione() e hai una goroutine; comunichi tra goroutine con i canali. Il modello è uniforme: lo stesso codice funziona in modo sincrono o concorrente, senza librerie esterne.
In Rust hai due strade, e entrambe chiedono di più:
- Thread del sistema operativo, con il compilatore che verifica che non ci siano accessi concorrenti non sicuri alla memoria. È la famosa "concorrenza senza paura": le race condition sui dati semplicemente non compilano.
- Programmazione asincrona con
async/await, che però richiede di scegliere un runtime esterno e introduce una complessità propria. L'async di Rust è potente ma è considerato, anche da chi lo apprezza, una delle parti più difficili del linguaggio.
In una riga: Go rende la concorrenza facile da scrivere, Rust la rende difficile da sbagliare. Per un servizio di rete con tante connessioni, la facilità di Go di solito vince. Per codice concorrente dove un errore costa carissimo, la rigidità di Rust vale il prezzo.
Dove trovi l'uno e dove l'altro
| Ambito | Scelta tipica | Perché |
|---|---|---|
| API e microservizi | Go | Produttività, deploy semplice, concorrenza facile |
| Strumenti per container e infrastruttura | Go | L'ecosistema di Docker e Kubernetes è in Go |
| Strumenti da riga di comando | Entrambi | Binario unico in tutti e due i casi |
| Motori di database, storage | Rust | Controllo della memoria, nessuna pausa |
| Sistemi embedded | Rust | Niente runtime, niente garbage collector |
| Browser, sistemi operativi, componenti critici per la sicurezza | Rust | Garanzie sulla memoria senza pagarle a runtime |
| Codice nel browser via WebAssembly | Rust | Supporto più maturo e binari più piccoli |
| Parti di un progetto in C o C++ da mettere in sicurezza | Rust | Si integra col codice esistente |
Nota la terza riga: su un'utility da terminale i due sono davvero equivalenti. Il binario unico, senza dipendenze da installare, è un vantaggio che condividono.
Il mercato del lavoro
Da tenere presente se la scelta riguarda la tua carriera e non un progetto.
Go ha più offerte in ambito backend e infrastruttura, e spesso compare insieme a competenze cloud e DevOps. Rust ha meno posizioni, più concentrate in aziende di prodotto tecnico, sicurezza e sistemi — ed è molto richiesto come competenza aggiuntiva da chi lavora già in C o C++.
In Italia nessuno dei due è un linguaggio di primo ingresso: le offerte junior sono poche per entrambi, e quasi sempre il datore di lavoro cerca chi li affianca a qualcos'altro. I numeri cambiano in fretta, quindi prima di decidere guarda le offerte reali nella tua zona e nel settore che ti interessa — il quadro generale sulle retribuzioni è in quanto guadagna un backend developer.
Errori di valutazione frequenti
Scegliere Rust per un'API normale "perché è più veloce". È l'errore più comune. Il collo di bottiglia sarà il database, e avrai pagato mesi di curva di apprendimento per un vantaggio che non misurerai mai.
Scartare Rust perché "è difficile". È difficile all'inizio, e la difficoltà è concentrata nei primi mesi. Se il progetto durerà anni e un bug di memoria ha conseguenze serie, quel costo iniziale si ripaga.
Pensare che Go sia "Rust semplificato" o Rust "Go per esperti". Sono stati progettati con obiettivi quasi opposti: Go per ridurre la complessità di un'organizzazione, Rust per eliminare intere categorie di errori. Non sono due gradini della stessa scala.
Decidere guardando i benchmark di un altro. Un benchmark misura un carico specifico. Se il tuo carico è diverso — e quasi sempre lo è — il risultato non ti dice niente.
Ignorare chi manterrà il codice. Un servizio Rust scritto benissimo da una persona che poi se ne va, in un team che non conosce Rust, è un problema. Il linguaggio va scelto anche in base alle persone che ci lavoreranno tra due anni.
Come decidere, in pratica
Fatti quattro domande, nell'ordine:
- Una pausa di qualche millisecondo o qualche megabyte in più sono un problema reale per questo software? Se no, Go è la scelta di partenza.
- Un errore di memoria qui ha conseguenze gravi — sicurezza, dati corrotti, dispositivi fisici? Se sì, Rust merita il costo.
- Chi scriverà e manterrà il codice, e quanto tempo ha per imparare? Un team con scadenze strette e rotazione alta spinge verso Go.
- In che ecosistema stai lavorando? Se estendi strumenti di infrastruttura cloud, sei già in territorio Go. Se integri codice C o C++, sei in territorio Rust.
Se dopo queste quattro domande sei ancora in dubbio, il caso è semplice: scegli Go. È la scelta più economica da correggere. Riscrivere in Rust un componente Go che si è rivelato critico è un progetto fattibile; il contrario — scoprire di aver speso mesi su Rust per un servizio che non ne aveva bisogno — non si recupera.
In sintesi
Rust e Go non competono sulla velocità, competono su dove mettono il costo: Go lo mette a runtime, con un garbage collector e controlli più permissivi; Rust lo mette sulle persone, con una curva di apprendimento ripida e un compilatore severo.
Go vince su produttività, concorrenza semplice e facilità di formare un team. È la scelta naturale per API, microservizi e strumenti di infrastruttura, dove il tempo di sviluppo pesa più dell'ultimo millisecondo.
Rust vince su prevedibilità, uso della memoria e sicurezza. È la scelta giusta per sistemi, embedded, motori e componenti dove un errore o una pausa costano più dei mesi spesi per impararlo.
Nel dubbio, Go, perché l'errore di averlo scelto è il più economico da correggere.
Se stai valutando anche altre strade, la mappa completa è in tutti i linguaggi di programmazione, e il confronto con il predecessore che Rust vuole sostituire è in cos'è C++.
Contenuto redatto con l'assistenza di strumenti di intelligenza artificiale e rivisto dalla redazione di Codegrind, che ne è responsabile. Hai trovato un errore? Scrivici.