Sintassi di Base in Rust
La sintassi di Rust è progettata per essere chiara, esplicita e sicura. Sebbene prenda ispirazione da linguaggi come C e C++, Rust introduce concetti unici che lo distinguono. In questa guida esploreremo gli elementi fondamentali della sintassi di Rust.
La Funzione main
Ogni programma Rust eseguibile deve avere una funzione main, che rappresenta il punto di ingresso dell’applicazione:
fn main() {
println!("Benvenuto in Rust!");
}
La parola chiave fn dichiara una funzione. Le parentesi graffe {} delimitano il corpo della funzione. A differenza di linguaggi come C, la funzione main in Rust non restituisce un intero per default, anche se è possibile farle restituire un Result.
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Main con Result!");
Ok(())
}
Statement ed Espressioni
In Rust, la distinzione tra statement (istruzioni) ed espressioni è fondamentale.
Statement
Uno statement esegue un’azione ma non restituisce un valore. Le dichiarazioni di variabili con let sono statement:
fn main() {
let x = 5; // Statement: dichiarazione di variabile
let y = 10; // Statement: dichiarazione di variabile
println!("{}", x + y);
}
Non è possibile assegnare uno statement a una variabile:
fn main() {
// let x = (let y = 5); // Errore! `let y = 5` è uno statement
}
Espressioni
Un’espressione restituisce un valore. In Rust, quasi tutto è un’espressione:
fn main() {
let x = 5 + 3; // 5 + 3 è un'espressione che vale 8
let y = {
let a = 10;
a * 2 // Nessun punto e virgola: è un'espressione
}; // y vale 20
println!("x = {}, y = {}", x, y);
}
Anche if è un’espressione in Rust:
fn main() {
let condizione = true;
let numero = if condizione { 5 } else { 10 };
println!("numero = {}", numero); // Stampa: numero = 5
}
Il Ruolo del Punto e Virgola
In Rust, il punto e virgola ; ha un significato preciso: trasforma un’espressione in uno statement, scartando il valore restituito.
fn cinque() -> i32 {
5 // Espressione: restituisce 5
}
fn niente() {
5; // Statement: il valore 5 viene scartato
}
fn main() {
let a = cinque();
println!("a = {}", a); // Stampa: a = 5
niente(); // Non restituisce nulla (restituisce il tipo unit `()`)
}
Questa distinzione è fondamentale: dimenticare o aggiungere un punto e virgola può cambiare completamente il comportamento del codice.
Blocchi di Codice e Scope
Un blocco di codice delimitato da parentesi graffe {} crea un nuovo scope (ambito di visibilitĂ ):
fn main() {
let x = 10;
{
let y = 20;
println!("Dentro il blocco: x = {}, y = {}", x, y);
} // y esce dallo scope qui
// println!("{}", y); // Errore! y non esiste piĂą
println!("Fuori dal blocco: x = {}", x);
}
I blocchi sono anche espressioni e restituiscono l’ultima espressione contenuta:
fn main() {
let risultato = {
let base = 10;
let altezza = 5;
base * altezza // Valore restituito dal blocco
};
println!("Area = {}", risultato); // Stampa: Area = 50
}
Convenzioni di Denominazione
Rust ha convenzioni di denominazione ben definite che il compilatore stesso segnala se non vengono rispettate:
snake_case per funzioni e variabili
fn calcola_area(base: f64, altezza: f64) -> f64 {
base * altezza
}
fn main() {
let numero_studenti = 30;
let area_rettangolo = calcola_area(5.0, 3.0);
println!("Studenti: {}, Area: {}", numero_studenti, area_rettangolo);
}
CamelCase per tipi e trait
struct PuntoCartesiano {
x: f64,
y: f64,
}
enum ColoreSemaforo {
Rosso,
Giallo,
Verde,
}
fn main() {
let punto = PuntoCartesiano { x: 1.0, y: 2.0 };
let colore = ColoreSemaforo::Verde;
}
SCREAMING_SNAKE_CASE per costanti
const VELOCITA_LUCE: f64 = 299_792_458.0;
const MAX_GIOCATORI: u32 = 100;
fn main() {
println!("La velocità della luce è {} m/s", VELOCITA_LUCE);
}
Le Macro con il Punto Esclamativo
In Rust, le chiamate che terminano con ! sono macro, non funzioni. Le macro sono un meccanismo di metaprogrammazione che genera codice a tempo di compilazione:
fn main() {
println!("Questa è una macro!"); // Stampa con newline
print!("Senza newline "); // Stampa senza newline
eprintln!("Errore su stderr!"); // Stampa su stderr
let v = vec![1, 2, 3]; // Macro per creare un Vec
let s = format!("Il vettore ha {} elementi", v.len()); // Formattazione
println!("{}", s);
// La macro todo!() è utile come placeholder
// todo!("Implementare questa funzione");
// La macro dbg!() è utile per il debug
let x = 42;
dbg!(x); // Stampa: [src/main.rs:14] x = 42
}
Le macro si distinguono dalle funzioni perché possono accettare un numero variabile di argomenti e generare codice durante la compilazione.
Conclusione
La sintassi di Rust è progettata per essere esplicita e prevenire errori comuni. La distinzione tra statement ed espressioni, il ruolo significativo del punto e virgola e le convenzioni di denominazione rigorose contribuiscono a rendere il codice Rust leggibile e manutenibile. Comprendere questi concetti fondamentali è il primo passo per padroneggiare il linguaggio e sfruttare appieno il suo sistema di tipi e le sue garanzie di sicurezza.