Pubblicare un Crate su Crates.io
Abbiamo utilizzato i pacchetti di crates.io come dipendenze del nostro progetto, ma puoi anche condividere il tuo codice con altre persone pubblicando i tuoi pacchetti. Il registro dei crate di crates.io distribuisce il codice sorgente dei tuoi pacchetti, quindi ospita principalmente codice open source.
Rust e Cargo hanno delle funzioni che rendono il tuo pacchetto pubblicato più facile da trovare e da usare. Parleremo di alcune di queste funzioni e poi spiegheremo come pubblicare un pacchetto.
Nota di traduzione: Quando si realizza documentazione è buona pratica che sia scritta in un linguaggio internazionale come l’inglese. In questo capitolo si è deciso di tradurre anche la documentazione del codice in italiano per facilitare la comprensione, ma se vorrai pubblicare codice e la relativa documentazione è consigliabile farlo in inglese.
Commentare il Codice a Fini di Documentazione
Documentare accuratamente i tuoi pacchetti aiuterà gli altri utenti a sapere
come e quando usarli, quindi vale la pena investire del tempo per scrivere la
documentazione. Nel Capitolo 3 abbiamo parlato di come commentare il codice di
Rust usando due barre, //
. Rust ha anche un particolare tipo di commento per
la documentazione, noto come commento di documentazione, che genererà la
documentazione HTML. L’HTML mostra il contenuto dei commenti di documentazione
per gli elementi API pubblici destinati ai programmatori interessati a sapere
come usare il tuo crate piuttosto che come il tuo crate è implementato.
I commenti di documentazione utilizzano tre barre, ///
, invece di due e
supportano la notazione Markdown per la formattazione del testo. Posiziona i
commenti di documentazione subito prima dell’elemento che stanno documentando.
Il Listato 14-1 mostra i commenti di documentazione per una funzione più_uno
in un crate chiamato mio_crate
/// Aggiunge uno al numero dato.
///
/// # Esempi
///
/// ```
/// let arg = 5;
/// let risposta = mio_crate::più_uno(arg);
///
/// assert_eq!(6, risposta);
/// ```
pub fn più_uno(x: i32) -> i32 {
x + 1
}
Qui diamo una descrizione di cosa fa la funzione più_uno
, iniziamo una sezione
con l’intestazione Esempi
(Examples) e poi forniamo del codice che dimostra
come utilizzare la funzione più_uno
. Possiamo generare la documentazione HTML
da questo commento di documentazione eseguendo cargo doc
. Questo comando
esegue lo strumento rustdoc
distribuito con Rust e mette la documentazione
HTML generata nella cartella target/doc.
Per comodità, eseguendo cargo doc --open
si costruisce l’HTML della
documentazione del tuo crate attuale (così come la documentazione di tutte le
dipendenze del tuo crate) e si apre il risultato in un browser web. Naviga
alla funzione più_uno
per vedere che il testo nei commenti di documentazione
apparirà come mostrato nella Figura 14-1.
Figura 14-1: La documentazione HTML per la funzione
più_uno
Sezioni Comunemente Utilizzate
Abbiamo usato l’intestazione Markdown # Esempi
nel Listato 14-1 per creare una
sezione nell’HTML con il titolo “Esempi”. Ecco altre sezioni che gli autori di
crate utilizzano comunemente nella loro documentazione:
- Panic (Panics): Questi sono gli scenari in cui la funzione documentata potrebbe andare in panic. I chiamanti della funzione che non vogliono che i loro programmi vadano in panic devono assicurarsi di non chiamare la funzione in queste situazioni.
- Errori (Errors): Se la funzione restituisce un
Result
, descrivere la tipologia di errori che potrebbero verificarsi e quali condizioni potrebbero causare la restituzione di tali errori può essere utile ai chiamanti, in modo che possano scrivere codice per gestire i diversi tipi di errore in modi diversi. - Sicurezza (Safety): Se la funzione è
unsafe
(ne parliamo nel Capitolo 20), deve essere presente una sezione che spieghi perché la funzione non è sicura e che descriva gli invarianti che la funzione si aspetta che i chiamanti rispettino.
La maggior parte dei commenti di documentazione non ha bisogno di tutte queste sezioni, ma questa è una buona lista da controllare per ricordarti gli aspetti del tuo codice che gli utenti saranno interessati a conoscere.
Commenti di Documentazione Come Test
L’aggiunta di blocchi di codice di esempio nei commenti di documentazione può
aiutare a dimostrare l’uso della libreria e ha un ulteriore vantaggio:
l’esecuzione di cargo test
eseguirà gli esempi di codice nella documentazione
come test! Non c’è niente di meglio di una documentazione con esempi, ma non c’è
niente di peggio di esempi che non funzionano perché il codice è cambiato da
quando è stata scritta la documentazione. Se eseguiamo cargo test
con la
documentazione della funzione più_uno
del Listato 14-1, vedremo una sezione
nei risultati del test che assomiglia a questa:
Doc-tests mio_crate
running 1 test
test src/lib.rs - più_uno (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s
Ora, se modifichiamo la funzione o l’esempio in modo che assert_eq!
nell’esempio vada in panic, ed eseguiamo di nuovo cargo test
, vedrai che i
Doc-tests rileveranno che l’esempio e il codice non sono compatibili tra loro!
Commentare l’Elemento Contenitore
Lo stile dei commenti di documentazione //!
aggiunge la documentazione
all’elemento che contiene i commenti piuttosto che agli elementi che seguono
i commenti. Di solito usiamo questi commenti di documentazione all’interno del
file radice del crate (src/lib.rs per convenzione) o all’interno di un
modulo per documentare il crate o il modulo nel suo complesso.
Ad esempio, per aggiungere la documentazione che descrive lo scopo del crate
mio_crate
che contiene la funzione più_uno
, aggiungiamo i commenti di
documentazione che iniziano con //!
all’inizio del file src/lib.rs, come
mostrato nel Listato 14-2.
//! # Mio Crate
//!
//! `mio_crate` è una collezione di funzioni per compiere
//! certi calcoli più facilmente.
/// Aggiunge uno al numero dato.
// --taglio--
///
/// # Esempi
///
/// ```
/// let arg = 5;
/// let risposta = mio_crate::più_uno(arg);
///
/// assert_eq!(6, risposta);
/// ```
pub fn più_uno(x: i32) -> i32 {
x + 1
}
mio_crate
Nota che non c’è codice dopo l’ultima riga che inizia con //!
. Poiché abbiamo
iniziato i commenti con //!
invece che con //
, stiamo documentando
l’elemento che contiene questo commento piuttosto che un elemento che segue
questo commento. In questo caso, questo elemento è il file src/lib.rs stesso,
che è la radice del crate. Questi commenti descrivono quindi l’intero crate.
Quando si esegue cargo doc --open
, questi commenti verranno visualizzati nella
prima pagina della documentazione di mio_crate
sopra l’elenco degli elementi
pubblici del crate, come mostrato nella Figura 14-2.
I commenti di documentazione all’interno degli elementi sono utili soprattutto per descrivere i crate e i moduli. Utilizzali per spiegare lo scopo generale del contenitore per aiutare i tuoi utenti a capire l’organizzazione del crate.
Figura 14-2: La documentazione renderizzata per
mio_crate
, incluso il commento che descrive il crate nel suo
complesso
Esportare un API Pubblica Efficace
La struttura della tua API pubblica è una considerazione importante quando pubblichi un crate. Le persone che usano il tuo crate hanno meno familiarità con la struttura di te e potrebbero avere difficoltà a trovare i pezzi che vogliono usare se il tuo crate ha una gerarchia di moduli estesa.
Nel Capitolo 7 abbiamo visto come rendere pubblici gli elementi con la parola
chiave pub
e come portare gli elementi nello scope con la parola chiave
use
. Tuttavia, la struttura che ha senso per te mentre sviluppi un crate
potrebbe non essere molto comoda per i tuoi utenti. Potresti voler organizzare
le tue struct in una gerarchia che contiene più livelli, ma chi vuole usare un
type che hai definito in profondità nella gerarchia potrebbe avere problemi a
scoprire l’esistenza di quel type. Potrebbe anche essere infastidito dal fatto
di dover scrivere use mio_crate::un_modulo::altro_modulo::TypeUtile;
piuttosto
che use mio_crate::TypeUtile;
.
La buona notizia è che se la struttura non è facile per gli altri da usare da
un’altra libreria, non devi modificare la tua organizzazione interna: puoi
invece riesportare gli elementi per creare una struttura pubblica diversa da
quella privata usando pub use
. Riesportare prende un elemento pubblico in
una posizione e lo rende pubblico in un’altra posizione, come se fosse stato
definito nell’altra posizione.
Ad esempio, supponiamo di aver creato una libreria chiamata arte
per modellare
concetti artistici. All’interno di questa libreria ci sono due moduli: un modulo
tipologia
contenente due enum chiamate ColorePrimario
e ColoreSecondario
e un modulo utilità
contenente una funzione chiamata mix
, come mostrato nel
Listato 14-3.
//! # Arte
//!
//! Una libreria per modellare concetti artistici.
pub mod tipologia {
/// I colori primari secondo il modello RYB.
pub enum ColorePrimario {
Rosso,
Giallo,
Blu,
}
/// I colori secondari secondo il modello RYB.
pub enum ColoreSecondario {
Arancio,
Verde,
Viola,
}
}
pub mod utilità {
use crate::tipologia::*;
/// Combina due colori primari in egual quantità
/// per formare un colore secondario.
pub fn mix(c1: ColorePrimario, c2: ColorePrimario) -> ColoreSecondario {
// --taglio--
unimplemented!();
}
}
arte
con elementi organizzati nei moduli tipologia
e utilità
La Figura 14-3 mostra l’aspetto della prima pagina della documentazione di
questo crate generata da cargo doc
.
Figura 14-3: La prima pagina della documentazione per
arte
che elenca i moduli tipologia
e utilità
Nota che i type ColorePrimario
e ColoreSecondario
non sono elencati nella
prima pagina, così come la funzione mix
. Dobbiamo cliccare su tipologia
e
utilità
per vederli.
Un altro crate che dipende da questa libreria avrebbe bisogno di dichiarazioni
use
che portino gli elementi di arte
nello scope, specificando la
struttura del modulo attualmente definita. Il Listato 14-4 mostra un esempio di
crate che utilizza gli elementi ColorePrimario
e mix
del crate arte
.
use arte::tipologia::ColorePrimario;
use arte::utilità::mix;
fn main() {
let rosso = ColorePrimario::Rosso;
let giallo = ColorePrimario::Giallo;
mix(rosso, giallo);
}
arte
con la sua struttura interna esportataL’autore del codice del Listato 14-4, che utilizza il crate arte
, ha dovuto
capire che ColorePrimario
si trova nel modulo tipologia
e mix
nel modulo
utilità
. La struttura dei moduli del crate arte
è più importante per gli
sviluppatori che lavorano sul crate arte
che per quelli che lo utilizzano.
La struttura interna non contiene informazioni utili per chi cerca di capire
come utilizzare il crate arte
, ma piuttosto crea confusione perché gli
sviluppatori che lo utilizzano devono capire dove cercare e devono specificare i
nomi dei moduli nelle dichiarazioni use
.
Per rimuovere l’organizzazione interna dall’API pubblica, possiamo modificare il
codice nel crate arte
del Listato 14-3 per aggiungere le dichiarazioni pub use
per riesportare gli elementi al livello superiore, come mostrato nel
Listato 14-5.
//! # Arte
//!
//! Una libreria per modellare concetti artistici.
pub use self::tipologia::ColorePrimario;
pub use self::tipologia::ColoreSecondario;
pub use self::utilità::mix;
pub mod tipologia {
// --taglio--
/// I colori primari secondo il modello RYB.
pub enum ColorePrimario {
Rosso,
Giallo,
Blu,
}
/// I colori secondari secondo il modello RYB.
pub enum ColoreSecondario {
Arancio,
Verde,
Viola,
}
}
pub mod utilità {
// --taglio--
use crate::tipologia::*;
/// Combina due colori primari in egual quantità
/// per formare un colore secondario.
pub fn mix(c1: ColorePrimario, c2: ColorePrimario) -> ColoreSecondario {
ColoreSecondario::Arancio
}
}
pub use
per riesportare elementiLa documentazione API che cargo doc
genera per questo crate ora elencherà e
collegherà le riesportazioni (Re-exports) nella prima pagina, come mostrato
nella Figura 14-4, rendendo più facile trovare i type ColorePrimario
e
ColoreSecondario
e la funzione mix
.
Figura 14-4: La prima pagina della documentazione per
arte
che elenca le riesportazioni
Gli utenti del crate arte
possono ancora vedere e usare la struttura interna
del Listato 14-3, come dimostrato nel Listato 14-4, oppure possono usare la
struttura più comoda del Listato 14-5, come mostrato nel Listato 14-6.
use arte::ColorePrimario;
use arte::mix;
fn main() {
// --taglio--
let rosso = ColorePrimario::Rosso;
let giallo = ColorePrimario::Giallo;
mix(rosso, giallo);
}
arte
Nei casi in cui ci sono molti moduli annidati, riesportare i type al livello
superiore con pub use
può fare una differenza significativa nell’esperienza
delle persone che utilizzano il crate. Un altro uso comune di pub use
è
quello di riesportare le definizioni di una dipendenza nel crate corrente per
rendere le definizioni di quel crate parte dell’API pubblica del tuo crate.
Creare una struttura API pubblica efficace è più un’arte che una scienza e puoi
iterare per trovare l’API che funziona meglio per i tuoi utenti. Scegliere pub use
ti dà flessibilità nel modo in cui strutturi il tuo crate internamente e
disaccoppia la struttura interna da quella che presenti ai tuoi utenti. Guarda
il codice di alcuni crate che hai installato per vedere se la loro struttura
interna differisce dalla loro API pubblica.
Creare un Account Crates.io
Prima di poter pubblicare qualsiasi crate, devi creare un account su
crates.io e ottenere un token API. Per
farlo, visita la home page di crates.io e accedi con un account GitHub
(attualmente l’account GitHub è un requisito, ma il sito potrebbe supportare
altri modi di creare un account in futuro). Una volta effettuato l’accesso,
visita le impostazioni del tuo account su
https://crates.io/me/ e genera la nuova
chiave API. Quindi esegui il comando cargo login
e incolla la tua chiave API
quando ti viene richiesto, in questo modo:
$ cargo login
abcdefghijklmnopqrstuvwxyz012345
Questo comando informerà Cargo del tuo token API e lo memorizzerà localmente in ~/.cargo/credentials.toml. Nota che questo token è segreto: non condividerlo con nessun altro. Se lo condividi con qualcuno per qualsiasi motivo, devi revocarlo e generare un nuovo token su crates.io.
Aggiungere Metadati a un Nuovo Crate
Supponiamo che tu abbia un crate che vuoi pubblicare. Prima di pubblicarlo,
dovrai aggiungere alcuni metadati nella sezione [package]
del file
Cargo.toml del crate.
Il tuo crate avrà bisogno di un nome univoco. Mentre stai lavorando su un
crate in locale, puoi dargli il nome che preferisci. Tuttavia, i nomi dei
crate su crates.io sono assegnati in base
all’ordine di arrivo. Una volta che il nome di un crate è stato preso, nessun
altro può pubblicare un crate con quel nome. Prima di provare a pubblicare un
crate, cerca il nome che vuoi usare. Se il nome è già stato usato, dovrai
trovarne un altro e modificare il campo name
nel file Cargo.toml sotto la
sezione [package]
per usare il nuovo nome per la pubblicazione, in questo
modo:
File: Cargo.toml
[package]
name = "gioco_indovinello"
Anche se hai scelto un nome unico, quando esegui cargo publish
per pubblicare
il crate a questo punto, riceverai un avviso e poi un errore simili a questo:
$ cargo publish
Updating crates.io index
warning: manifest has no description, license, license-file, documentation, homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
-- test compilazione --
error: failed to publish to registry at https://crates.io
Caused by:
the remote server responded with an error (status 400 Bad Request): missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for more information on configuring these fields
Questo genera un errore perché mancano alcune informazioni cruciali: una
descrizione e una licenza sono necessarie affinché le persone sappiano cosa fa
la il tuo crate e a quali condizioni possono utilizzarla. In Cargo.toml,
aggiungi una descrizione che sia solo una frase o due, perché apparirà insieme
al tuo crate nei risultati di ricerca. Per il campo license
, devi indicare
un valore identificativo della licenza. Il Linux Foundation’s Software
Package Data Exchange (SPDX) elenca gli identificativi che puoi usare per
questo valore. Per esempio, per specificare che hai concesso in licenza il tuo
crate usando la MIT License, aggiungi l’identificativo MIT
:
File: Cargo.toml
[package]
name = "gioco_indovinello"
license = "MIT"
Se vuoi usare una licenza che non compare nell’SPDX, devi inserire il testo
della licenza in un file, includere il file nel tuo progetto e poi usare
license-file
per specificare il nome del file invece di usare la chiave
license
.
Orientarti su quale licenza sia appropriata per il tuo progetto va oltre lo
scopo di questo libro. Molte persone nella comunità di Rust concedono la licenza
per i loro progetti nello stesso modo di Rust, utilizzando una doppia licenza
MIT OR Apache-2.0
. Questa pratica dimostra che puoi anche specificare più
identificatori di licenza separati da OR
per avere più licenze per il tuo
progetto.
Con un nome univoco, la versione, la tua descrizione e la licenza aggiunta, il file Cargo.toml per un progetto pronto per la pubblicazione potrebbe assomigliare a questo
File: Cargo.toml
[package]
name = "gioco_indovinello"
version = "0.1.0"
edition = "2024"
description = "Un giochino divertente dove tenti di indovinare un numero casuale."
license = "MIT OR Apache-2.0"
[dependencies]
Nella documentazione di Cargo trovi la descrizione di altri metadati che puoi specificare per far sì che gli altri possano scoprire e utilizzare il tuo crate più facilmente.
Pubblicare su Crates.io
Ora che hai creato un account, salvato il tuo token API, scelto un nome per il tuo crate e specificato i metadati richiesti, sei pronto a pubblicare! Pubblicare un crate carica una versione specifica su crates.io affinché altri possano utilizzarla.
Fai attenzione, perché una pubblicazione è permanente. La versione non può mai essere sovrascritta e il codice non può essere cancellato se non in determinate circostanze. Uno degli obiettivi principali di Crates.io è quello di fungere da archivio permanente del codice in modo che le compilazioni di tutti i progetti che dipendono dai crate di crates.io continuino a funzionare. Consentire la cancellazione delle versioni renderebbe impossibile il raggiungimento di questo obiettivo. Tuttavia, non c’è limite al numero di versioni del crate che puoi pubblicare.
Esegui di nuovo il comando cargo publish
: ora dovrebbe andare a buon fine:
$ cargo publish
Updating crates.io index
Packaging gioco_indovinello v0.1.0 (file:///progetti/gioco_indovinello)
Packaged 6 files, 1.2KiB (895.0B compressed)
Verifying gioco_indovinello v0.1.0 (file:///progetti/gioco_indovinello)
Compiling gioco_indovinello v0.1.0
(file:///progetti/gioco_indovinello/target/package/gioco_indovinello-0.1.0)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
Uploading gioco_indovinello v0.1.0 (file:///progetti/gioco_indovinello)
Uploaded gioco_indovinello v0.1.0 to registry `crates-io`
note: waiting for `gioco_indovinello v0.1.0` to be available at registry
`crates-io`.
You may press ctrl-c to skip waiting; the crate should be available shortly.
Published gioco_indovinello v0.1.0 at registry `crates-io`
Congratulazioni, ora hai condiviso il tuo codice con la comunità di Rust e chiunque può facilmente aggiungere il tuo crate come dipendenza del proprio progetto.
Pubblicare una Nuova Versione di un Crate
Quando hai apportato delle modifiche al tuo crate e sei pronto a rilasciare
una nuova versione, cambia il valore version
specificato nel file Cargo.toml
e ripubblica. Utilizza le regole di Versionamento Semantico per
decidere quale sia il numero di versione successivo più appropriato, in base
alla tipologia di modifiche apportate. Quindi esegui cargo publish
per
caricare la nuova versione.
Deprecare Versioni da Crates.io
Sebbene non sia possibile rimuovere le versioni precedenti di un crate, puoi impedire a qualsiasi progetto futuro di aggiungerle come nuova dipendenza. Questo è utile quando una versione del crate è mal funzionante per un motivo o per l’altro. In queste situazioni, Cargo supporta la disabilitazione di una versione del crate.
Disabilitare una versione impedisce ai nuovi progetti di dipendere da quella versione, mentre permette a tutti i progetti esistenti che dipendono da essa di continuare. In sostanza, una disabilitazione significa che tutti i progetti con un Cargo.lock non si romperanno e tutti i futuri file Cargo.lock generati non utilizzeranno la versione disabilitata.
Per disabilitare una versione del crate, nella directory del crate che hai
pubblicato in precedenza, esegui cargo yank
e specifica quale versione vuoi
disabilitare. Ad esempio, se hai pubblicato un crate chiamato
gioco_indovinello
nella versione 1.0.1 e vuoi disabilitarla, nella directory
del progetto per gioco_indovinello
dovrai eseguire:
$ cargo yank --vers 1.0.1
Updating crates.io index
Yank gioco_indovinello@1.0.1
Aggiungendo --undo
al comando, puoi anche annullare una disabilitazione, in
pratica riabilitare, e permettere ai progetti di ricominciare a dipendere da
quella versione:
$ cargo yank --vers 1.0.1 --undo
Updating crates.io index
Unyank gioco_indovinello@1.0.1
Quando disabiliti una versione non cancelli alcun codice. Non puoi, ad esempio, cancellare dati sensibili (chiavi API, password, ecc.) che per errore hai caricato accidentalmente. Se ciò accadesse, devi immediatamente ripristinare e cambiare quei dati sensibili.