Perché Rust
L'utilizzo del linguaggio di programmazione Rust potrebbe sembrare a prima vista una complicazione inutile del processo di sviluppo, soprattutto per la sua nota curva di apprendimento che non ne favorisce un utilizzo immediato (o almeno non come potrebbe fare un linguaggio di programmazione come Python). Una volta però acquisita padronanza con la sua sintassi e con il suo sistema di tipi, ci offre molti vantaggi.
- Memory safety: il compilatore riesce a garantire a tempo di compilazione la correttezza di tutte le operazioni in memoria.
- Alto livello di astrazione: il linguaggio è molto ricco sintatticamente e semanticamente, e permette di programmare ad alto livello. Sono incluse astrazioni simili a interfacce (i trait), tipi generici e il linguaggio ha un ricco supporto per la metaprogrammazione tramite le macro.
- Performance: grazie al backend LLVM e alle assunzioni derivate dal sistema di tipi, il compilatore Rust riesce a produrre binari che competono con linguaggi a basso livello come C e C++.
- Ecosistema: l'ecosistema di Rust è molto ricco di strumenti per l'analisi del codice e molte librarie che implementano delle funzionalità standard sono pubblicate sotto forma di crates.
Vediamo adesso più nel dettaglio due di queste proprietà.
Memory safety
Come abbiamo visto nel modulo di sicurezza a basso livello, la presenza di bug di corruzione della memoria possono essere molto pericolosi per un applicazione, in molti casi permettono a un attaccante, tramite un apposito input, di eseguire codice arbitrario nel contesto dell'applicazione. Questa classe di bug può essere evitata in un modo molto comune, ovvero usando un linguaggio che non permette esplicitamente la gestione della memoria a basso livello. Questo paradigma si basa sull'aggiunta di un gestore della memoria a tempo di esecuzione del programma, che combinato con un garbage collector gestisce automaticamente la distruzione di oggetti non più utilizzati. Molti linguaggi di programmazione molto popolari quali Java, Python, C# e molti altri adottano questo meccanismo. Il vantaggio di usare un garbage collector è la flessibilità e la semplicità di gestione della memoria da parte del programmatore, ma il principale svantaggio di questo sistema è che incorre in overhead a tempo di esecuzione.
Rust adotta un sistema di ownership dei valori, in particolare il compilatore riesce a "tracciare" il ciclo di vita degli oggetti, e riesce a inserire a tempo di compilazione il codice di allocazione e distruzione. Queste meccanismo è alla base del linguaggio, ed è anche la differenza più evidente sperimentando per la prima volta con Rust. Esiste comunque la possibilità di allocare "manualmente" oggetti sull'heap, ma anche in questo caso Rust ci fornisce delle astrazioni (i cosiddetti Smart pointers) per garantire la memory safety.
Alto livello di astrazione
Arrivando da un linguaggio di alto livello come Java o C#, Rust non manca di astrazioni, in particolare:
- permette di definire strutture e metodi (anche se non è strettamente un linguaggio orientato agli oggetti),
- permette l'uso di astrazioni derivanti dai linguaggi funzionali, quali le enumerazioni e l'uso di funzioni di ordine superiore,
- permette la definizione di trait (simili a interfacce in Java, ma con un meccanismo diverso d'instanziazione) per la definizione di attributi e metodi comuni a più strutture,
- ha un ricco ecosistema di meta programmazione tramite macro,
e molto altro.