SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI
Percorsi per Riferirsi a un Elemento nell’Albero dei Moduli
Per indicare a Rust dove trovare un elemento in un albero dei moduli, utilizziamo un percorso allo stesso modo in cui usiamo un percorso durante la navigazione di un filesystem. Per chiamare una funzione, abbiamo bisogno di conoscere il suo percorso.
Un percorso può assumere due forme:
- Un percorso assoluto è il percorso completo a partire da una radice del crate; per il codice proveniente da un crate esterno, il percorso assoluto inizia con il nome del crate, e per il codice proveniente dal crate corrente, inizia con il letterale crate.
- Un percorso relativo parte dal modulo corrente e utilizza self, super o un identificatore nel modulo corrente.
Sia i percorsi assoluti che relativi sono seguiti da uno o più identificatori separati da doppi due punti (::).
Tornando all’elenco 7-1, diciamo che vogliamo chiamare la funzione add_to_waitlist. Questo è lo stesso che chiedere: qual è il percorso della funzione add_to_waitlist? L’elenco 7-3 contiene l’elenco 7-1 con alcuni dei moduli e delle funzioni rimossi.
Mostreremo due modi per chiamare la funzione add_to_waitlist da una nuova funzione eat_at_restaurant definita nella radice del crate. Questi percorsi sono corretti, ma c’è ancora un altro problema che impedirà a questo esempio di compilare così com’è. Spiegheremo il motivo tra poco.
La funzione eat_at_restaurant fa parte dell’API pubblica del nostro crate libreria, quindi la marichiamo con la parola chiave pub. Nella sezione “Esposizione dei Percorsi con la parola chiave pub”, approfondiremo ulteriormente il pub.
rust
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
} pub fn eat_at_restaurant() {
// Percorso assoluto
crate::front_of_house::hosting::add_to_waitlist();
// Percorso relativo
front_of_house::hosting::add_to_waitlist();
}
La prima volta che chiamiamo la funzione add_to_waitlist in eat_at_restaurant, utilizziamo un percorso assoluto. La funzione add_to_waitlist è definita nello stesso crate di eat_at_restaurant, il che significa che possiamo usare la parola chiave crate per iniziare un percorso assoluto. Includiamo quindi ciascuno dei moduli successivi fino a raggiungere add_to_waitlist. Puoi immaginare un filesystem con la stessa struttura: specificare il percorso /front_of_house/hosting/add_to_waitlist per eseguire il programma add_to_waitlist; utilizzare il nome del crate per iniziare dalla radice del crate è come usare / per iniziare dalla radice del filesystem nel tuo shell.
La seconda volta che chiamiamo add_to_waitlist in eat_at_restaurant, utilizziamo un percorso relativo. Il percorso inizia con front_of_house, il nome del modulo definito allo stesso livello dell’albero dei moduli di eat_at_restaurant. Qui l’equivalente nel filesystem sarebbe utilizzare il percorso front_of_house/hosting/add_to_waitlist. Iniziare con il nome del modulo significa che il percorso è relativo.
Scegliere se utilizzare un percorso relativo o assoluto è una decisione che prenderai in base al tuo progetto e dipende dal fatto che sia più probabile che sposti il codice di definizione dell’elemento separatamente o insieme al codice che utilizza l’elemento. Ad esempio, se spostiamo il modulo front_of_house e la funzione eat_at_restaurant in un modulo chiamato customer_experience, dovremmo aggiornare il percorso assoluto per add_to_waitlist, ma il percorso relativo sarebbe ancora valido. Tuttavia, se spostiamo la funzione eat_at_restaurant separatamente in un modulo chiamato dining, il percorso assoluto per la chiamata add_to_waitlist rimarrebbe lo stesso, ma il percorso relativo dovrebbe essere aggiornato. La nostra preferenza in generale è specificare percorsi assoluti perché è più probabile che vogliamo spostare le definizioni del codice e le chiamate degli elementi indipendentemente l’una dall’altra.
Proviamo a compilare l’elenco 7-3 e scopriamo perché non si compilerà ancora! Gli errori che otteniamo sono mostrati nell’elenco 7-4.
Esposizione dei Percorsi con la parola chiave pub
Torniamo all’errore nell’Elenco 7-4 che ci ha detto che il modulo hosting è privato. Vogliamo che la funzione eat_at_restaurant nel modulo genitore abbia accesso alla funzione add_to_waitlist nel modulo figlio, quindi contrassegniamo il modulo hosting con la parola chiave pub, come mostrato nell’Elenco 7-5.
rust
mod front_of_house {
pub mod hosting {
fn add_to_waitlist() {}
}
} pub fn eat_at_restaurant() {
// Percorso assoluto
crate::front_of_house::hosting::add_to_waitlist();
// Percorso relativo
front_of_house::hosting::add_to_waitlist();
}
Sfortunatamente, il codice nell’Elenco 7-5 produce comunque un errore, come mostrato nell’Elenco 7-6.
less
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:9:37
|
9 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^ error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:12:30
|
12 | front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` due to 2 previous errors
Cosa è successo? Aggiungere la parola chiave pub davanti a mod hosting rende il modulo pubblico. Con questa modifica, se possiamo accedere a front_of_house, possiamo accedere a hosting. Ma il contenuto di hosting è ancora privato; rendere il modulo pubblico non rende il suo contenuto pubblico. La parola chiave pub su un modulo consente solo al codice nei suoi moduli antenati di fare riferimento ad esso, non di accedere al suo codice interno. Poiché i moduli sono contenitori, non c’è molto che possiamo fare rendendo solo il modulo pubblico; dobbiamo andare oltre e scegliere di rendere pubblici uno o più degli elementi all’interno del modulo.
Gli errori nell’Elenco 7-6 dicono che la funzione add_to_waitlist è privata. Le regole sulla privacy si applicano anche a strutture, enumerazioni, funzioni e metodi oltre che ai moduli.
Facciamo diventare anche la funzione add_to_waitlist pubblica aggiungendo la parola chiave pub prima della sua definizione, come nell’Elenco 7-7.
rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
} pub fn eat_at_restaurant() {
// Percorso assoluto
crate::front_of_house::hosting::add_to_waitlist();
// Percorso relativo
front_of_house::hosting::add_to_waitlist();
}
Ora il codice verrà compilato! Per capire perché l’aggiunta della parola chiave pub ci consente di utilizzare questi percorsi in add_to_waitlist rispetto alle regole sulla privacy, esaminiamo i percorsi assoluti e relativi.
Nel percorso assoluto, partiamo da crate, la radice dell’albero dei moduli del nostro crate. Il modulo front_of_house è definito nella radice del crate. Anche se front_of_house non è pubblico, perché la funzione eat_at_restaurant è definita nello stesso modulo di front_of_house (cioè eat_at_restaurant e front_of_house sono fratelli), possiamo fare riferimento a front_of_house da eat_at_restaurant. Successivamente c’è il modulo hosting contrassegnato con pub. Possiamo accedere al modulo genitore di hosting, quindi possiamo accedere a hosting. Infine, la funzione add_to_waitlist è contrassegnata con pub e possiamo accedere al suo modulo genitore, quindi questa chiamata di funzione funziona!
Nel percorso relativo, la logica è la stessa del percorso assoluto tranne per il primo passo: anziché partire dalla radice del crate, il percorso parte da front_of_house. Il modulo front_of_house è definito all’interno dello stesso modulo in cui è definito eat_at_restaurant, quindi il percorso relativo che inizia dal modulo in cui è definito eat_at_restaurant funziona. Quindi, poiché hosting e add_to_waitlist sono contrassegnati con pub, il resto del percorso funziona e questa chiamata di funzione è valida!
Se prevedi di condividere il tuo crate libreria in modo che altri progetti possano usare il tuo codice, la tua API pubblica è il tuo contratto con gli utenti del tuo crate che determina come possono interagire con il tuo codice. Ci sono molte considerazioni sulla gestione delle modifiche alla tua API pubblica per rendere più semplice per le persone dipendere dal tuo crate. Queste considerazioni escono dal campo di applicazione di questo libro; se sei interessato a questo argomento, consulta le Linee Guida API di Rust.
Migliori Pratiche per Pacchetti con un Binario e un Libreria
Abbiamo menzionato che un pacchetto può contenere sia un binario src/main.rs crate root che una libreria src/lib.rs crate root, e entrambi i crate avranno il nome del pacchetto per impostazione predefinita. Tipicamente, i pacchetti con questo pattern di contenere sia un crate di libreria che un crate binario avranno solo abbastanza codice nel crate binario per avviare un eseguibile che chiama il codice con il crate di libreria. Questo consente ad altri progetti di beneficiare della maggior parte delle funzionalità che il pacchetto fornisce, perché il codice del crate di libreria può essere condiviso.
L’albero dei moduli dovrebbe essere definito in src/lib.rs. Quindi, gli elementi pubblici possono essere utilizzati nel crate binario iniziando i percorsi con il nome del pacchetto. Il crate binario diventa un utente del crate di libreria proprio come un crate completamente esterno userebbe il crate di libreria: può usare solo l’API pubblica. Questo ti aiuta a progettare una buona API; non sei solo l’autore, sei anche un cliente!
Nel Capitolo 12, dimostreremo questa pratica organizzativa con un programma da riga di comando che conterrà sia un crate binario che un crate di libreria.
Iniziare Percorsi Relativi con super
Possiamo costruire percorsi relativi che iniziano nel modulo genitore, piuttosto che nel modulo corrente o nella radice del crate, utilizzando super all’inizio del percorso. Questo è come iniziare un percorso del filesystem con la sintassi .. . Utilizzando super ci consente di fare riferimento a un elemento che sappiamo essere nel modulo genitore, il che può semplificare la riorganizzazione dell’albero dei moduli quando il modulo è strettamente correlato al genitore, ma il genitore potrebbe essere spostato altrove nell’albero dei moduli in futuro.
Considera il codice nell’Elenco 7-8 che modella la situazione in cui uno chef corregge un ordine errato e lo porta personalmente al cliente. La funzione fix_incorrect_order definita nel modulo back_of_house chiama la funzione deliver_order definita nel modulo genitore specificando il percorso per deliver_order che inizia con super:
rust
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
La funzione fix_incorrect_order è nel modulo back_of_house, quindi possiamo usare super per andare al modulo genitore di back_of_house, che in questo caso è il crate, la radice. Da lì, cerchiamo deliver_order e la troviamo. Successo! Pensiamo che il modulo back_of_house e la funzione deliver_order siano probabili rimanere nella stessa relazione tra loro e vengano spostati insieme nel caso decidiamo di riorganizzare l’albero dei moduli del crate. Pertanto, abbiamo usato super per avere meno luoghi in cui aggiornare il codice in futuro se questo codice viene spostato in un modulo diverso.
Rendere Struct ed Enum Pubblici
Possiamo anche utilizzare pub per designare strutture ed enumerazioni come pubbliche, ma ci sono alcuni dettagli extra nell’uso di pub con strutture ed enumerazioni. Se usiamo pub prima della definizione di una struttura, rendiamo la struttura pubblica, ma i campi della struttura saranno comunque privati. Possiamo rendere pubblici o meno ciascun campo caso per caso. Nell’Elenco 7-9, abbiamo definito una struct back_of_house::Breakfast pubblica con un campo toast pubblico ma un campo seasonal_fruit privato. Questo modella il caso in cui in un ristorante il cliente può scegliere il tipo di pane che accompagna un pasto, ma lo chef decide quale frutta accompagna il pasto in base a ciò che è di stagione e in stock. La frutta disponibile cambia rapidamente, quindi i clienti non possono scegliere la frutta o vedere quale frutta riceveranno.
rust
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
Poiché il campo toast nella struct back_of_house::Breakfast è pubblico, in eat_at_restaurant possiamo scrivere e leggere il campo toast usando la notazione a punto. Nota che non possiamo usare il campo seasonal_fruit in eat_at_restaurant perché seasonal_fruit è privato. Prova a decommentare la riga che modifica il valore del campo seasonal_fruit per vedere quale errore ottieni!
Inoltre, nota che poiché back_of_house::Breakfast ha un campo privato, la struct deve fornire una funzione associata pubblica che costruisce un’istanza di Breakfast (l’abbiamo chiamata summer qui). Se Breakfast non avesse una tale funzione, non potremmo creare un’istanza di Breakfast in eat_at_restaurant perché non potremmo impostare il valore del campo privato seasonal_fruit in eat_at_restaurant.
Al contrario, se rendiamo un enum pubblico, tutte le sue varianti diventano pubbliche. Abbiamo bisogno solo di pub prima della parola chiave enum, come mostrato nell’Elenco 7-10.
rust
mod back_of_house {
pub enum Appetizer {
Soup,
Salad,
}
}
Poiché abbiamo reso l’enum Appetizer pubblico, possiamo usare le varianti Soup e Salad in eat_at_restaurant.
Gli enum non sono molto utili a meno che le loro varianti non siano pubbliche; sarebbe fastidioso dover annotare tutte le varianti dell’enum con pub in ogni caso, quindi il default per le varianti dell’enum è essere pubbliche. Le strutture sono spesso utili senza che i loro campi siano pubblici, quindi i campi della struttura seguono la regola generale di essere privati per impostazione predefinita a meno che non siano annotati con pub.
C’è un’altra situazione che coinvolge pub che non abbiamo ancora coperto, ed è la nostra ultima caratteristica del sistema di moduli: la parola chiave use. Copriremo use da solo prima, e poi mostreremo come combinare pub e use.