Da subito, ci sono chiare differenze tra Go e Rust. Go si concentra maggiormente sulla costruzione di API web e piccoli servizi che possono scalare all’infinito, specialmente con la potenza delle goroutine. Quest’ultimo è possibile anche con Rust, ma le cose sono molto più difficili dal punto di vista dell’esperienza dello sviluppatore.

Rust funziona bene per l’elaborazione di grandi quantità di dati e altre operazioni che richiedono molta CPU, come l’esecuzione di algoritmi. Questo è il più grande vantaggio di Rust rispetto a Go. I progetti che richiedono alte prestazioni sono generalmente più adatti a Rust.

In questo tutorial, confronteremo e contrasteremo Go e Rust, valutando ogni linguaggio di programmazione per le prestazioni, la concorrenza, la gestione della memoria e l’esperienza complessiva dello sviluppatore. Presenteremo anche una panoramica di questi elementi per aiutarvi a scegliere il linguaggio giusto per il vostro progetto a colpo d’occhio.

Se avete appena iniziato con Rust, potrebbe essere una buona idea ripassare questa guida per principianti prima di continuare a leggere.

Se hai capito tutto, tuffiamoci!

Performance

Originariamente progettato dagli ingegneri di Google, Go è stato presentato al pubblico nel 2009. È stato creato per offrire un’alternativa al C++ che fosse più facile da imparare e codificare ed era ottimizzato per funzionare su CPU multicore.

Da allora, Go è stato grande per gli sviluppatori che vogliono trarre vantaggio dalla concorrenza che il linguaggio offre. Il linguaggio fornisce goroutine che permettono di eseguire funzioni come sottoprocessi.

Un grande vantaggio di Go è quanto facilmente si possono usare le goroutine. Semplicemente aggiungendo la sintassi go ad una funzione la si fa girare come un sottoprocesso. Il modello di concorrenza di Go permette di distribuire i carichi di lavoro su più core della CPU, rendendolo un linguaggio molto efficiente.

package mainimport ( "fmt" "time")func f(from string) { for i := 0; i < 3; i++ { fmt.Println(from, ":", i) }}func main() { f("direct") go f("goroutine") time.Sleep(time.Second) fmt.Println("done")}

Nonostante il supporto alla CPU multicore, Rust riesce ancora a superare Go. Rust è più efficiente nell’esecuzione di algoritmi e operazioni ad alta intensità di risorse. Il Benchmarks Game confronta Rust e Go per vari algoritmi, come gli alberi binari. Per tutti gli algoritmi testati, Rust era almeno il 30 per cento più veloce; nel caso dei calcoli degli alberi binari, era fino al 1.000 per cento. Uno studio di Bitbucket mostra risultati simili in cui Rust è alla pari con C++.

Prestazioni di Rust secondo Bitbucket

Prestazioni di Rust secondo Bitbucket

(Fonte: Benchmarks Game)

Concurrency

Come detto sopra, Go supporta la concurrency. Per esempio, diciamo che stai eseguendo un server web che gestisce le richieste API. È possibile utilizzare le goroutine di Go per eseguire ogni richiesta come un sottoprocesso, massimizzando l’efficienza scaricando i compiti su tutti i core della CPU disponibili.

Le goroutine fanno parte delle funzioni integrate di Go, mentre Rust ha ricevuto solo la sintassi nativa async/await per supportare la concorrenza. Come tale, il vantaggio dell’esperienza dello sviluppatore va a Go quando si tratta di concorrenza. Tuttavia, Rust è molto meglio nel garantire la sicurezza della memoria.

Ecco un esempio di thread semplificato per Rust:

use std::thread;use std::time::Duration;fn main() { // 1. create a new thread for i in 1..10 { thread::spawn(|| { println!("thread: number {}!", i); thread::sleep(Duration::from_millis(100)); }); } println!("hi from the main thread!");}

La concorrenza è sempre stata un problema spinoso per gli sviluppatori. Non è un compito facile garantire una concorrenza memory-safe senza compromettere l’esperienza dello sviluppatore. Tuttavia, questa estrema attenzione alla sicurezza ha portato alla creazione di una concorrenza provatamente corretta. Rust ha sperimentato il concetto di proprietà per impedire l’accesso non richiesto alle risorse per prevenire i bug di sicurezza della memoria.

Rust offre quattro diversi paradigmi di concorrenza per aiutarvi ad evitare i comuni trabocchetti della sicurezza della memoria. Daremo uno sguardo più da vicino a due paradigmi comuni: il canale e il lock.

Channel

Un canale aiuta a trasferire un messaggio da un thread all’altro. Mentre questo concetto esiste anche per Go, Rust permette di trasferire un puntatore da un thread all’altro per evitare condizioni di corsa per le risorse. Attraverso il passaggio di puntatori, Rust può imporre l’isolamento dei thread per i canali. Ancora una volta, Rust mostra la sua ossessione per la sicurezza della memoria per quanto riguarda il suo modello di concorrenza.

Lock

I dati sono accessibili solo quando il lock è tenuto. Rust si basa sul principio di bloccare i dati invece del codice, che si trova spesso in linguaggi di programmazione come Java.

Per maggiori dettagli sul concetto di proprietà e su tutti i paradigmi di concorrenza, controlla “Fearless Concurrency with Rust.”

Sicurezza della memoria

Il precedente concetto di proprietà è uno dei punti di forza di Rust. Rust porta la sicurezza dei tipi, che è anche importante per abilitare la concurrency memory-safe, al livello successivo.

Secondo il blog di Bitbucket, “il compilatore molto rigoroso e pedante di Rust controlla ogni variabile che usi e ogni indirizzo di memoria che fai riferimento. Evita possibili condizioni di corsa dei dati e vi informa sul comportamento non definito.”

Questo significa che non vi ritroverete con un buffer overflow o una condizione di corsa a causa dell’estrema ossessione di Rust per la sicurezza della memoria. Tuttavia, questo ha anche i suoi svantaggi. Per esempio, bisogna essere iperconsapevoli dei principi di allocazione della memoria mentre si scrive il codice. Non è facile avere sempre la guardia alla sicurezza della memoria alzata.

Esperienza dello sviluppatore

Prima di tutto, guardiamo la curva di apprendimento associata a ciascun linguaggio. Go è stato progettato con la semplicità in mente. Gli sviluppatori spesso si riferiscono ad esso come ad un linguaggio “noioso”, il che significa che il suo insieme limitato di caratteristiche incorporate rende Go facile da adottare.

Inoltre, Go offre un’alternativa più facile al C++, nascondendo aspetti come la sicurezza e l’allocazione della memoria. Rust adotta un altro approccio, costringendovi a pensare a concetti come la sicurezza della memoria. Il concetto di proprietà e la capacità di passare puntatori rende Rust un’opzione meno attraente da imparare. Quando pensi costantemente alla sicurezza della memoria, sei meno produttivo e il tuo codice è destinato ad essere più complesso.

La curva di apprendimento di Rust è piuttosto ripida rispetto a Go. Vale la pena menzionare, tuttavia, che Go ha una curva di apprendimento più ripida rispetto a linguaggi più dinamici come Python e JavaScript.

Quando usare Go

Go funziona bene per un’ampia varietà di casi d’uso, rendendolo una grande alternativa a Node.js per creare API web. Come notato da Loris Cro, “il modello di concorrenza di Go si adatta bene alle applicazioni lato server che devono gestire più richieste indipendenti”. Questo è esattamente il motivo per cui Go fornisce le goroutines.

Inoltre, Go ha un supporto integrato per il protocollo web HTTP. È possibile progettare rapidamente una piccola API utilizzando il supporto HTTP integrato ed eseguirla come un microservizio. Pertanto, Go si adatta bene all’architettura dei microservizi e soddisfa le esigenze degli sviluppatori di API.

In breve, Go è una buona scelta se apprezzate la velocità di sviluppo e preferite la semplicità della sintassi rispetto alle prestazioni. Inoltre, Go offre una migliore leggibilità del codice, che è un criterio importante per i grandi team di sviluppo.

Scegliete Go quando:

  • Vi interessa la semplicità e la leggibilità
  • Volete una sintassi facile per scrivere velocemente il codice
  • Volete usare un linguaggio più flessibile che supporti lo sviluppo web

Quando usare Rust

Rust è una grande scelta quando le prestazioni contano, come quando state elaborando grandi quantità di dati. Inoltre, Rust ti dà un controllo preciso su come i thread si comportano e su come le risorse sono condivise tra i thread.

D’altra parte, Rust viene con una ripida curva di apprendimento e rallenta la velocità di sviluppo a causa della complessità extra della sicurezza della memoria. Questo non è necessariamente uno svantaggio; Rust garantisce anche che non incontrerete bug di sicurezza della memoria poiché il compilatore controlla ogni singolo puntatore di dati. Per sistemi complessi, questa garanzia può tornare utile.

Scegli Rust quando:

  • Ti interessa la performance
  • Vuoi un controllo a grana fine sui thread
  • Ti interessa la sicurezza della memoria rispetto alla semplicità

Go vs. Rust: La mia onesta opinione

Partiamo evidenziando le somiglianze. Sia Go che Rust sono open-source e progettati per supportare l’architettura microservizi e gli ambienti di calcolo parallelo. Entrambi ottimizzano l’utilizzo dei core della CPU disponibili attraverso la concorrenza.

Ma alla fine della giornata, qual è il linguaggio migliore?

Ci sono molti modi per affrontare questa domanda. Raccomanderei di pensare al tipo di applicazione che si vuole costruire. Go serve bene per la creazione di applicazioni web e API che sfruttano le sue caratteristiche di concorrenza incorporate mentre supportano l’architettura microservizi.

Si può anche usare Rust per sviluppare un’API web, ma non è stato progettato con questo caso d’uso in mente. L’attenzione di Rust sulla sicurezza della memoria aumenta la complessità e il tempo di sviluppo, specialmente per un’API web abbastanza semplice. Tuttavia, la maggiore quantità di controllo che hai sul tuo codice ti permette di scrivere codice più ottimizzato, efficiente in termini di memoria e performante.

Per dirla nel modo più semplice possibile, il dibattito Go contro Rust è davvero una questione di semplicità contro sicurezza.

Per altre prospettive, guarda “Scegliere tra Go e Rust.”

LogRocket: Visibilità completa nelle applicazioni Rust in produzione

Il debug delle applicazioni Rust può essere difficile, specialmente quando gli utenti sperimentano problemi difficili da riprodurre. Se sei interessato a monitorare e tracciare le prestazioni delle tue applicazioni Rust, a far emergere automaticamente gli errori e a tracciare le richieste di rete lente e il tempo di caricamento, prova LogRocket. LogRocket Dashboard Free Trial Banner

LogRocket è come un DVR per le applicazioni web, registrando letteralmente tutto ciò che accade sulla tua app Rust. Invece di indovinare perché i problemi si verificano, è possibile aggregare e riferire su quale stato era la vostra applicazione quando si è verificato un problema. LogRocket monitora anche le prestazioni della tua app, riportando metriche come il carico della CPU del client, l’utilizzo della memoria del client e altro ancora.

Modernizza il modo in cui esegui il debug delle tue app Rust – inizia il monitoraggio gratuitamente.

Articles

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.