In Rust è possibile utilizzare la costruzione type per creare un alias per un tipo esistente. Ad esempio:
type Kilometers = i32; fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { x + y }
Kilometers è semplicemente un altro nome per i32; il compilatore non distingue questi tipi e sono completamente intercambiabili.
Newtype pattern è la creazione di una nuova struttura di wrapping per un tipo esistente:
struct Kilometers(i32); // nuovo tipo separato fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { Kilometers(x.0 + y.0) }
Ora Kilometers e i32 sono tipi diversi, non possono essere confusi.
La scelta tra questi approcci dipende dai requisiti di sicurezza e leggibilità. Per API pubbliche e specialmente unità di misura sicure per tipo, utilizzare il newtype.
Qual è la differenza tra l'alias
typee il newtype pattern in Rust? È possibile limitare le operazioni per un alias di tipo attraverso l'implementazione di un trait?
Spesso si risponde che l'alias può essere usato per controllare l'accesso e limitare le operazioni, ma non è così: l'alias non genera un nuovo tipo, quindi non è possibile implementare per esso tratti unici. Solo il newtype pattern è un tipo separato, per il quale si può implementare un comportamento unico.
type UserId = u64; struct UserIdNew(u64); trait Foo { fn foo(&self); } // Non è possibile implementare Foo per UserId, perché è semplicemente un u64. // Per UserIdNew è possibile.
Storia
Nel progetto le distanze venivano misurate in metri e millimetri usando alias:
type Millimeters = u32; type Meters = u32;Nel codice finale si sommavano accidentalmente millimetri e metri, causando errori di calcolo. Il sistema di tipi non ha funzionato: il compilatore non ha catturato l'errore. Passando a newtype, il problema è scomparso.
Storia
In un'API pubblica si utilizzavano alias per gli identificatori di diverse entità (
type UserId = u64;,type OrderId = u64;). È sorto un malinteso: qualcuno ha scambiato i parametri e un bug è finito in produzione a causa dell'impossibilità di distinguerli tramite i tipi.
Storia
Uno sviluppatore ha cercato di implementare un comportamento unico del trait Display per un alias di tipo:
type MyType = String; impl Display for MyType { ... }Il compilatore ha restituito un errore: "non è possibile implementare un trait per un alias di tipo". Il problema è stato risolto tramite il newtype pattern.