Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
  • 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
  • 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 path crate::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 di mod. Per rendere pubblici anche gli elementi all’interno di un modulo pubblico, usa pub prima delle loro dichiarazioni.
  • La parola chiave use: All’interno di uno scope, la parola chiave use crea scorciatoie per gli elementi per ridurre la ripetizione di lunghi path. In qualsiasi scope che può fare riferimento a crate::giardino::verdure::Asparagi, puoi creare una scorciatoia con use crate::giardino::verdure::Asparagi; e da quel momento in poi devi scrivere solo Asparagi 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:

File: src/main.rs
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 è:

File: src/giardino.rs
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.

File: src/lib.rs
mod sala {
    mod accoglienza {
        fn aggiungi_in_lista() {}

        fn metti_al_tavolo() {}
    }

    mod servizio {
        fn prendi_ordine() {}

        fn servi_ordine() {}

        fn prendi_pagamento() {}
    }
}
Listato 7-1: Un modulo sala contenente altri moduli che poi contengono funzioni

Definiamo 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
Listato 7-2: L’albero dei moduli per il codice nel Listato 7-1

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.