Controllare Scope e Privacy con i Moduli
In questa sezione, parleremo dei moduli e di altre parti del sistema dei moduli,
in particolare dei path (percorsi), che ti permettono di nominare gli
elementi; la parola chiave use
che porta un path in scope; e la parola
chiave pub
per rendere pubblici gli elementi. Discuteremo anche della parola
chiave as
, dei pacchetti esterni e dell’operatore glob.
Scheda Informativa sui Moduli
Prima di entrare nei dettagli dei moduli e dei path, qui forniamo un rapido
riferimento su come funzionano i moduli, i path, la parola chiave use
e la
parola chiave pub
nel compilatore, e come la maggior parte degli sviluppatori
organizza il proprio codice. Esamineremo esempi di ciascuna di queste regole nel
corso di questo capitolo, ma questo è un ottimo riassunto da consultare come
promemoria su come funzionano i moduli.
- Inizia dalla radice del crate: Quando compili un crate, il compilatore prima cerca nel file di radice del crate (di solito src/lib.rs per un crate libreria e src/main.rs per un crate binario) il codice da compilare.
- Dichiarare moduli: Nel file di radice del crate, puoi dichiarare nuovi
moduli; ad esempio, dichiari un modulo “giardino” con
mod giardino;
. Il compilatore cercherà il codice del modulo in questi luoghi:- Sulla linea, all’interno delle parentesi graffe che sostituiscono il punto e
virgola dopo
mod giardino
- Nel file src/giardino.rs
- Nel file src/giardino/mod.rs
- Sulla linea, all’interno delle parentesi graffe che sostituiscono il punto e
virgola dopo
- Dichiarare sottomoduli: In qualsiasi file diverso dalla radice del
crate, puoi dichiarare sottomoduli. Ad esempio, potresti dichiarare
mod verdure;
in src/giardino.rs. Il compilatore cercherà il codice del sottomodulo all’interno della cartella nominata per il modulo genitore (parent) in questi luoghi:- Sulla linea, direttamente dopo
mod verdure
, all’interno delle parentesi graffe invece del punto e virgola - Nel file src/giardino/verdure.rs
- Nel file src/giardino/verdure/mod.rs
- Sulla linea, direttamente dopo
- Path per il codice nei moduli: Una volta che un modulo è parte del tuo
crate, puoi fare riferimento al codice in quel modulo da qualsiasi altro
punto dello stesso crate, purché le regole di privacy lo consentano,
utilizzando il path per il codice. Ad esempio, un type
Asparagi
nel modulo delle verdure del giardino si troverebbe al pathcrate::giardino::verdure::Asparagi
. - Privato vs. pubblico: Il codice all’interno di un modulo è non
utilizzabile, privato, dai suoi moduli genitore come impostazione
predefinita. Per rendere un modulo utilizzabile, pubblico, è necessario
dichiaralo con
pub mod
invece dimod
. Per rendere pubblici anche gli elementi all’interno di un modulo pubblico, usapub
prima delle loro dichiarazioni. - La parola chiave
use
: All’interno di uno scope, la parola chiaveuse
crea scorciatoie per gli elementi per ridurre la ripetizione di lunghi path. In qualsiasi scope che può fare riferimento acrate::giardino::verdure::Asparagi
, puoi creare una scorciatoia conuse crate::giardino::verdure::Asparagi;
e da quel momento in poi devi scrivere soloAsparagi
per utilizzare quel type nello scope.
Ora creiamo un crate binario chiamato cortile
che illustra queste regole.
La cartella del crate, anch’essa chiamata cortile, contiene questi file e
cartelle:
cortile
├── Cargo.lock
├── Cargo.toml
└── src
├── giardino
│ └── verdure.rs
├── giardino.rs
└── main.rs
La radice del crate in questo caso è src/main.rs, e contiene:
use crate::giardino::verdure::Asparagi;
pub mod giardino;
fn main() {
let pianta = Asparagi {};
println!("Sto coltivando {pianta:?}!");
}
La riga pub mod giardino;
dice al compilatore di includere il codice che trova
in src/giardino.rs, che è:
pub mod verdure;
Qui, pub mod verdure;
significa che il codice in src/giardino/verdure.rs è
incluso anch’esso. Quel codice è:
#[derive(Debug)]
pub struct Asparagi {}
Ora entriamo nei dettagli di queste regole e dimostriamo come funzionano!
Raggruppare Codice Correlato in Moduli
I moduli ci permettono di organizzare il codice all’interno di un crate per migliore leggibilità e facilità di riutilizzo. I moduli ci consentono anche di controllare la privacy degli elementi, poiché il codice all’interno di un modulo è privato come impostazione predefinita. Gli elementi privati sono dettagli di implementazione interni non disponibili per l’uso esterno. Possiamo scegliere di rendere pubblici i moduli e gli elementi al loro interno, il che li espone per consentire al codice esterno di utilizzarli e dipendere da essi.
Come esempio, scriviamo un crate libreria che fornisce la funzionalità di un ristorante. Definiremo le firme delle funzioni ma lasceremo i loro corpi vuoti per concentrarci sull’organizzazione del codice piuttosto che sull’implementazione vera e propria.
Nel settore della ristorazione, alcune “funzioni” di un ristorante sono chiamate sala e altre cucina. La “sala” è dove si trovano i clienti; questo comprende dove l’oste riceve i clienti, i camerieri prendono ordini e pagamenti, e i baristi preparano drink. La “cucina” è dove gli chef e i cuochi lavorano in cucina, i lavapiatti puliscono e i manager svolgono lavori amministrativi.
Per strutturare il nostro crate in questo modo, possiamo organizzare le sue
funzioni in moduli annidati. Crea una nuova libreria chiamata ristorante
eseguendo cargo new ristorante --lib
. Poi inserisci il codice nel Listato 7-1
in src/lib.rs per definire alcuni moduli e firme di funzione; questo codice è
la sezione sala.
mod sala {
mod accoglienza {
fn aggiungi_in_lista() {}
fn metti_al_tavolo() {}
}
mod servizio {
fn prendi_ordine() {}
fn servi_ordine() {}
fn prendi_pagamento() {}
}
}
sala
contenente altri moduli che poi contengono funzioniDefiniamo un modulo con la parola chiave mod
seguita dal nome del modulo (in
questo caso, sala
). Il corpo del modulo va quindi all’interno delle parentesi
graffe. All’interno dei moduli, possiamo inserire altri moduli, come in questo
caso con i moduli accoglienza
e servizio
. I moduli possono anche contenere
definizioni per altri elementi, come struct, enum, costanti, trait e, come
nel Listato 7-1, funzioni.
Utilizzando i moduli, possiamo raggruppare definizioni correlate insieme e nominare il motivo per cui sono correlate. I programmatori che utilizzano questo codice possono navigare nel codice in base ai gruppi piuttosto che dover leggere tutte le definizioni, rendendo più facile trovare le definizioni rilevanti per loro. I programmatori che aggiungono nuove funzionalità a questo codice saprebbero dove posizionare il codice per mantenere organizzato il programma.
In precedenza, abbiamo menzionato che src/main.rs e src/lib.rs sono chiamati
radici del crate. Il motivo del loro nome è che i contenuti di uno di questi
due file formano un modulo chiamato crate
alla radice della struttura del
modulo del crate, nota come albero dei moduli (module tree).
Il Listato 7-2 mostra l’albero dei moduli per la struttura nel Listato 7-1.
crate
└── sala
├── accoglienza
│ ├── aggiungi_in_lista
│ └── metti_al_tavolo
└── servizio
├── prendi_ordine
├── servi_ordine
└── prendi_pagamento
Questo albero mostra come alcuni dei moduli si annidano all’interno di altri
moduli; ad esempio, accoglienza
si annida all’interno di sala
. L’albero
mostra anche che alcuni moduli sono fratelli, il che significa che sono
definiti nello stesso modulo; accoglienza
e servizio
sono fratelli definiti
all’interno di sala
. Se il modulo A è contenuto all’interno del modulo B,
diciamo che il modulo A è il figlio del modulo B e che il modulo B è il
genitore del modulo A. Nota che l’intero albero dei moduli è radicato sotto il
modulo implicito chiamato crate
.
L’albero dei moduli potrebbe ricordarti l’albero delle cartelle del filesystem sul tuo computer; questo è un confronto molto appropriato! Proprio come le cartelle in un filesystem, usi i moduli per organizzare il tuo codice. E proprio come i file in una cartella, abbiamo bisogno di un modo per trovare i nostri moduli.