Spazi di Lavoro Cargo
Nel Capitolo 12 abbiamo costruito un pacchetto che includeva un crate binario e un crate libreria. Man mano che il tuo progetto si sviluppa, potresti scoprire che il crate libreria continua a diventare più grande e vorresti dividere ulteriormente il tuo pacchetto in più crate libreria. Cargo offre una funzione chiamata spazi di lavoro (workspace) che può aiutarti a gestire più pacchetti correlati che vengono sviluppati in tandem.
Creare un Workspace
Uno spazio di lavoro è un insieme di pacchetti che condividono lo stesso
Cargo.lock e la stessa directory di output. Creiamo un progetto utilizzando un
workspace: useremo del codice banale in modo da poterci concentrare sulla
struttura del workspace. Esistono diversi modi per strutturare uno spazio di
lavoro, quindi ci limiteremo a mostrarne uno comune. Avremo un workspace
contenente un binario e due librerie. Il binario, che fornirà la funzionalità
principale, dipenderà dalle due librerie. Una libreria fornirà una funzione
più_uno
e l’altra libreria una funzione più_due
. Questi tre crate faranno
parte dello stesso workspace. Inizia creando una nuova cartella per lo spazio
di lavoro:
Nota di traduzione: per i nomi di crate non è possibile utilizzare caratteri non-ASCII, a differenza della possibilità di utilizzo nei nomi di funzione. Si è scelto di usare quindi sia
piu_uno
chepiù_uno
per rimarcare questa differenza. Fai quindi attenzione.
$ mkdir somma
$ cd somma
Successivamente, nella cartella somma, creiamo il file Cargo.toml che
configurerà l’intero workspace. Questo file non avrà una sezione [package]
,
ma inizierà con una sezione [workspace]
che ci permetterà di aggiungere membri
al workspace. Inoltre, esplicitiamo di voler usare l’ultima versione
dell’algoritmo di risoluzione delle dipendenze di Cargo nel nostro spazio di
lavoro, impostando il valore resolver
a "3"
:
File: Cargo.toml
[workspace]
resolver = "3"
Successivamente, creeremo il crate binario sommatore
eseguendo cargo new
nella cartella somma:
$ cargo new sommatore
Created binary (application) `sommatore` package
Adding `sommatore` as member of workspace at `file:///progetti/somma`
L’esecuzione di cargo new
all’interno di uno spazio di lavoro aggiunge
automaticamente il pacchetto appena creato alla chiave members
nella
definizione [workspace]
del file Cargo.toml, in questo modo:
[workspace]
resolver = "3"
members = ["sommatore"]
A questo punto, possiamo costruire l’intero workspace con cargo build
. I
file nella tua cartella somma dovrebbero avere questo aspetto:
somma
├── Cargo.lock
├── Cargo.toml
├── sommatore
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
Lo spazio di lavoro ha una cartella target al livello superiore in cui
verranno inseriti gli artefatti compilati; il pacchetto sommatore
non ha una
propria cartella target. Anche se dovessimo eseguire cargo build
dall’interno della cartella sommatore, gli artefatti compilati finirebbero
comunque in somma/target piuttosto che in somma/sommatore/target. Cargo
struttura la cartella target in uno spazio di lavoro in questo modo perché i
crate in un workspace sono destinati a dipendere l’uno dall’altro. Se ogni
crate avesse la propria cartella target, ogni crate dovrebbe ricompilare
ogni altro crate nello spazio di lavoro per posizionare gli artefatti nella
propria cartella target. Condividendo una cartella target, i crate possono
evitare inutili ricostruzioni.
Creare un Secondo Pacchetto nel Workspace
Ora creiamo un altro pacchetto membro dell’area di lavoro e chiamiamolo
piu_uno
. Generiamo un nuovo crate libreria chiamato piu_uno
:
$ cargo new piu_uno --lib
Created library `piu_uno` package
Adding `piu_uno` as member of workspace at `file:///progetti/somma`
Il file Cargo.toml nella cartella somma ora includerà il percorso piu_uno
nell’elenco dei membri members
:
File: Cargo.toml
[workspace]
resolver = "3"
members = ["sommatore", "piu_uno"]
La tua cartella somma dovrebbe ora contenere queste cartelle e questi file:
somma
├── Cargo.lock
├── Cargo.toml
├── piu_uno
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── sommatore
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
Nel file piu_uno/src/lib.rs, aggiungiamo una funzione più_uno
:
File: piu_uno/src/lib.rs
pub fn più_uno(x: i32) -> i32 {
x + 1
}
Ora possiamo fare in modo che il pacchetto sommatore
con il nostro binario
dipenda dal pacchetto piu_uno
che contiene la nostra libreria. Per prima cosa,
dovremo aggiungere un percorso di dipendenza a piu_uno
nel file
sommatore/Cargo.toml.
File: sommatore/Cargo.toml
[dependencies]
piu_uno = { path = "../piu_uno" }
Cargo non presuppone che i crate dello stesso workspace dipendano l’uno dall’altro, quindi dobbiamo essere espliciti sulle relazioni di dipendenza.
Quindi, utilizziamo la funzione più_uno
(dal crate piu_uno
) nel crate
sommatore
. Apri il file sommatore/src/main.rs e modifica la funzione main
per richiamare la funzione più_uno
, come nel Listato 14-7
fn main() {
let num = 10;
println!("Ciao! {num} più uno fa {}!", piu_uno::più_uno(num));
}
piu_uno
dal crate sommatore
Compiliamo lo spazio di lavoro eseguendo cargo build
nella directory di primo
livello somma!
$ cargo build
Compiling piu_uno v0.1.0 (file:///progetti/somma/piu_uno)
Compiling sommatore v0.1.0 (file:///progetti/somma/sommatore)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
Per eseguire il crate binario dalla directory somma, possiamo specificare
quale pacchetto del workspace vogliamo eseguire utilizzando l’argomento -p
e
il nome del pacchetto con cargo run
:
$ cargo run -p sommatore
Compiling sommatore v0.1.0 (file:///progetti/somma/sommatore)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/sommatore`
Ciao! 10 più uno fa 11!
Questo esegue il codice in sommatore/src/main.rs, che dipende dal crate
piu_uno
.
Dipendere da un Pacchetto Esterno
Avrai notato che lo spazio di lavoro ha un solo file Cargo.lock al livello
superiore, invece di avere un Cargo.lock nella cartella di ogni crate.
Questo assicura che tutti i crate utilizzino la stessa versione di tutte le
dipendenze. Se aggiungiamo il pacchetto rand
ai file sommatore/Cargo.toml e
piu_uno/Cargo.toml, Cargo li risolverà entrambi in un’unica versione di rand
e la registrerà nell’unico Cargo.lock. Fare in modo che tutti i crate nel
workspace utilizzino le stesse dipendenze significa che i crate saranno
sempre compatibili tra loro. Aggiungiamo il crate rand
alla sezione
[dependencies]
nel file piu_uno/Cargo.toml in modo da poter utilizzare il
crate rand
nel crate piu_uno
:
File: piu_uno/Cargo.toml
[dependencies]
rand = "0.8.5"
Ora possiamo aggiungere use rand;
al file piu_uno/src/lib.rs e la creazione
dell’intero workspace eseguendo cargo build
nella cartella somma
scaricherà e compilerà il crate rand
. Riceveremo un avviso perché non stiamo
effettivamente usando rand
che abbiamo portato nello scope:
$ cargo build
Updating crates.io index
Downloaded rand v0.8.5
--taglio--
Compiling rand v0.8.5
Compiling piu_uno v0.1.0 (file:///progetti/somma/piu_uno)
warning: unused import: `rand`
--> piu_uno/src/lib.rs:1:5
|
1 | use rand;
| ^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: `piu_uno` (lib) generated 1 warning (run `cargo fix --lib -p piu_uno` to apply 1 suggestion)
Compiling sommatore v0.1.0 (file:///progetti/somma/sommatore)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s
Il file Cargo.lock al livello più alto ora contiene informazioni sulla
dipendenza di piu_uno
da rand
. Tuttavia, anche se rand
è utilizzato da
qualche parte nello spazio di lavoro, non possiamo utilizzarlo in altri crate
del workspace a meno che non aggiungiamo rand
anche ai loro file
Cargo.toml. Ad esempio, se aggiungiamo use rand;
al file
sommatore/src/main.rs per il pacchetto sommatore
, otterremo un errore:
$ cargo build
--taglio--
Compiling sommatore v0.1.0 (file:///progetti/somma/sommatore)
error[E0432]: unresolved import `rand`
--> sommatore/src/main.rs:2:5
|
2 | use rand;
| ^^^^ no external crate `rand`
Per risolvere questo problema, modifica il file Cargo.toml per il pacchetto
sommatore
e indica che rand
è una dipendenza anche per esso. Costruendo il
pacchetto sommatore
aggiungerà rand
all’elenco delle dipendenze di
sommatore
in Cargo.lock, ma non verranno scaricate copie aggiuntive di
rand
. Cargo farà in modo che ogni crate in ogni pacchetto dell’area di
lavoro che utilizza il pacchetto rand
utilizzi la stessa versione, a patto che
specifichi versioni compatibili di rand
, risparmiando spazio e assicurando che
i crate nel workspace siano compatibili tra loro.
Se i crate nel workspace specificano versioni incompatibili della stessa dipendenza, Cargo risolverà ciascuna di esse, ma cercherà comunque di risolvere il minor numero possibile di versioni.
Aggiungere un Test a un Workspace
Per un altro miglioramento, aggiungiamo un test della funzione
piu_uno::più_uno
all’interno del crate piu_uno
:
File: piu_uno/src/lib.rs
pub fn più_uno(x: i32) -> i32 {
x + 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn funziona() {
assert_eq!(3, più_uno(2));
}
}
Ora esegui cargo test
nella cartella di primo livello somma. Eseguendo
cargo test
in un workspace strutturato come questo, verranno eseguiti i test
per tutti i crate presenti nello spazio di lavoro:
$ cargo test
Compiling piu_uno v0.1.0 (file:///progetti/somma/piu_uno)
Compiling sommatore v0.1.0 (file:///progetti/somma/sommatore)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s
Running unittests src/lib.rs (target/debug/deps/piu_uno-93c49ee75dc46543)
running 1 test
test tests::funziona ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/sommatore-3a47283c568d2b6a)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests piu_uno
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
La prima sezione dell’output mostra che il test funziona
nel crate piu_uno
è passato. La sezione successiva mostra che sono stati trovati zero test nel
crate sommatore
e l’ultima sezione mostra che sono stati trovati zero test
di documentazione nel crate piu_uno
.
Possiamo anche eseguire i test per un particolare crate in un workspace
dalla directory di primo livello utilizzando il flag -p
e specificando il
nome del crate che vogliamo testare:
$ cargo test -p piu_uno
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.00s
Running unittests src/lib.rs (target/debug/deps/piu_uno-93c49ee75dc46543)
running 1 test
test tests::funziona ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests piu_uno
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Questo output mostra che cargo test
ha eseguito solo i test del crate
piu_uno
e non ha eseguito i test del crate sommatore
.
Se pubblichi i crate nello workspace su crates.io, ogni crate nello spazio di lavoro dovrà essere pubblicato
separatamente. Come nel caso di cargo test
, possiamo pubblicare un particolare
crate nel nostro spazio di lavoro utilizzando il flag -p
e specificando il
nome del crate che vogliamo pubblicare.
Per fare ulteriore pratica, aggiungi un crate piu_due
a questo spazio di
lavoro in modo simile al crate piu_uno
!
Quando il tuo progetto cresce, prendi in considerazione l’utilizzo di uno spazio di lavoro: ti permette di lavorare con componenti più piccoli e più facili da capire rispetto a un unico grande blocco di codice. Inoltre, mantenere i crate in uno spazio di lavoro può rendere più facile il coordinamento tra i crate se questi vengono spesso modificati nello stesso momento.