ProgrammazioneSviluppatore Backend

Come vengono realizzati gli "alias di tipo" in Rust, quali sono le differenze rispetto ai nuovi tipi (newtype pattern) e perché è importante comprendere la differenza nella progettazione delle API?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

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.

  • Alias sono utili per la leggibilità o API generalizzate.
  • Newtype pattern fornisce sicurezza di tipo e consente di implementare tratti unici per i nuovi tipi.

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.

Domanda insidiosa.

Qual è la differenza tra l'alias type e 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.

Esempi di errori pratici a causa della scarsa comprensione dell'argomento.


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.