Moduli in Rust
Finora ogni esempio è stato in un unico file main.rs. Per i programmi piccoli va benissimo, ma i progetti veri dividono il codice in pezzi logici. Lo strumento di Rust per farlo è il sistema dei moduli: mod, pub e use.
Definire un modulo
Sezione intitolata “Definire un modulo”Un modulo è un blocco con un nome che raggruppa codice collegato:
mod math { pub fn add(a: i32, b: i32) -> i32 { a + b }}
fn main() { let sum = math::add(2, 3); println!("{sum}");}Output:
5Due cose da notare: la funzione si chiama con math::add(...), usando il nome del modulo come percorso. Ed è segnata come pub: senza, non sarebbe raggiungibile da fuori del modulo.
Tutto è privato di default
Sezione intitolata “Tutto è privato di default”Vale la pena sottolinearlo, perché sorprende molti: il contenuto di un modulo è privato di default. Se togli pub dall’esempio qui sopra, non compila più:
mod math { fn add(a: i32, b: i32) -> i32 { // niente `pub` a + b }}
fn main() { let sum = math::add(2, 3); // ❌ errore: la funzione `add` è privata println!("{sum}");}Devi segnare esplicitamente con pub tutto quello che vuoi far usare a chi sta fuori dal modulo. È una scelta voluta: permette a un modulo di nascondere le sue funzioni di supporto interne e di mostrare solo le parti pensate come API pubblica.
Moduli annidati
Sezione intitolata “Moduli annidati”I moduli possono contenere altri moduli: è così che i progetti più grandi costruiscono una struttura ad albero:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
fn main() { let area = shapes::circle::area(2.0); println!("{area}");}use: accorciare i percorsi lunghi
Sezione intitolata “use: accorciare i percorsi lunghi”Scrivere ogni volta shapes::circle::area(...) diventa noioso. use porta un percorso nello scope, così puoi riferirti a lui direttamente:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
use shapes::circle;
fn main() { let area = circle::area(2.0); // più corto di shapes::circle::area println!("{area}");}In realtà hai già usato use: è la stessa parola chiave della pagina su HashMap (use std::collections::HashMap;).
Dividere i moduli su più file
Sezione intitolata “Dividere i moduli su più file”Gli esempi qui sopra mettono tutto in un solo file, come richiede il Rust Playground. In un progetto vero sul tuo computer, di solito ogni modulo ha il suo file. Se avessi un progetto con un modulo math, di solito sarebbe fatto così:
src/├── main.rs└── math.rsmod math; // dice a Rust: cerca il codice di questo modulo in math.rs
fn main() { let sum = math::add(2, 3); println!("{sum}");}pub fn add(a: i32, b: i32) -> i32 { a + b}mod math; (con il punto e virgola, senza corpo) dice a Rust di caricare il contenuto del modulo da math.rs nella stessa cartella. È lo schema che userai appena un progetto supera un singolo file.
Pratica
Sezione intitolata “Pratica”1. Un modulo per le temperature
Sezione intitolata “1. Un modulo per le temperature”Crea un modulo chiamato convert con due funzioni pubbliche: c_to_f (da Celsius a Fahrenheit) e f_to_c (al contrario). Da main, stampa 100°C sono 212°F e 212°F sono 100°C.
Suggerimento: le formule sono c * 9.0 / 5.0 + 32.0 e (f - 32.0) * 5.0 / 9.0. Non dimenticare pub, e chiama le funzioni con il nome del modulo, come convert::c_to_f(100.0).
Mostra la soluzione
mod convert { pub fn c_to_f(c: f64) -> f64 { c * 9.0 / 5.0 + 32.0 }
pub fn f_to_c(f: f64) -> f64 { (f - 32.0) * 5.0 / 9.0 }}
fn main() { println!("100°C sono {}°F", convert::c_to_f(100.0)); println!("212°F sono {}°C", convert::f_to_c(212.0));}Output:
100°C sono 212°F212°F sono 100°CEntrambe le funzioni sono pub, quindi main (che sta fuori dal modulo) può chiamarle. Togli un pub e il compilatore ti dirà che quella funzione è privata.
2. Una funzione di supporto privata
Sezione intitolata “2. Una funzione di supporto privata”Crea un modulo annidato school::grades con una funzione pubblica letter(score: u32) -> char che restituisce 'A' da 90 in su, 'B' da 80 in su e 'C' negli altri casi. Dentro grades scrivi anche una funzione di supporto privata is_at_least(score: u32, min: u32) -> bool che letter usa. Porta school::grades nello scope con use, poi stampa la lettera per 95, 85 e 70.
Suggerimento: sia il mod grades interno a school sia fn letter hanno bisogno di pub, ma is_at_least non deve averlo. Il codice dentro lo stesso modulo può sempre chiamare le funzioni private.
Mostra la soluzione
mod school { pub mod grades { pub fn letter(score: u32) -> char { if is_at_least(score, 90) { 'A' } else if is_at_least(score, 80) { 'B' } else { 'C' } }
// privata: solo il codice dentro `grades` può chiamarla fn is_at_least(score: u32, min: u32) -> bool { score >= min } }}
use school::grades;
fn main() { for score in [95, 85, 70] { println!("{score} -> {}", grades::letter(score)); }}Output:
95 -> A85 -> B70 -> Cletter può chiamare is_at_least perché stanno nello stesso modulo. Da main, grades::is_at_least(95, 90) fallirebbe con «function is_at_least is private» (la funzione è privata). È proprio questo il punto: il modulo mostra una piccola API pubblica e tiene per sé le sue funzioni di supporto.
Riepilogo
Sezione intitolata “Riepilogo”mod nome { ... }raggruppa codice collegato in un modulo con un nome.- Tutto quello che sta dentro un modulo è privato di default: aggiungi
pubper renderlo visibile fuori dal modulo. useporta un percorso nello scope, così non devi scriverlo per intero ogni volta.- In un progetto su più file,
mod nome;(senza corpo) carica il codice di quel modulo dal file corrispondente. - Il codice dentro un modulo può sempre usare gli elementi privati di quel modulo, quindi tieni private le funzioni di supporto e mostra solo quello che serve a chi chiama.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Di default, gli elementi dentro un mod sono pubblici o privati?
2. Come dici a Rust di caricare il codice di un modulo da math.rs invece di scriverlo direttamente lì?
Punteggio: 0 / 2
Per approfondire
Sezione intitolata “Per approfondire”- Managing Growing Projects with Packages, Crates, and Modules — The Rust Book (in inglese): il capitolo ufficiale su pacchetti, crate e moduli, e su come si incastrano tra loro.
- Defining Modules to Control Scope and Privacy — The Rust Book (in inglese): un esempio completo di albero di moduli, con un comodo riassunto delle regole.
- Separating Modules into Different Files — The Rust Book (in inglese): la struttura su più file in maggiore dettaglio, comprese le cartelle per i moduli annidati.
- Modules — Rust by Example (in inglese): esempi eseguibili su visibilità,
use,supereself. - Visibility and privacy — The Rust Reference (in inglese): le regole precise, compreso
pub(crate)per «pubblico solo dentro il mio crate».