En Rust, on peut utiliser la construction type pour créer un alias pour un type existant. Par exemple :
type Kilometers = i32; fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { x + y }
Kilometers est simplement un autre nom pour i32 ; le compilateur ne distingue pas ces types, ils sont entièrement interchangeables.
Le newtype pattern consiste à créer une nouvelle structure d'encapsulation pour un type existant :
struct Kilometers(i32); // un nouveau type séparé fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { Kilometers(x.0 + y.0) }
Maintenant, Kilometers et i32 sont des types différents, on ne peut pas les confondre.
Le choix entre ces approches dépend des exigences en matière de sécurité et de lisibilité. Pour des API ouvertes et en particulier pour des unités de mesure de type sécurisées, utilisez le newtype.
Quelle est la différence entre un alias
typeet un newtype pattern en Rust ? Peut-on restreindre les opérations pour un alias de type via l'implémentation d'un trait ?
On répond souvent qu'un alias peut être utilisé pour contrôler l'accès et restreindre les opérations, mais ce n'est pas vrai — un alias ne crée pas un nouveau type, donc on ne peut pas implémenter de traits uniques pour lui. Seul le newtype pattern est un type distinct, pour lequel un comportement unique peut être implémenté.
type UserId = u64; struct UserIdNew(u64); trait Foo { fn foo(&self); } // On ne peut pas implémenter Foo pour UserId car c'est juste un u64. // Pour UserIdNew — c'est possible.
Histoire
Dans un projet, les distances étaient mesurées en mètres et millimètres à l'aide d'alias :
type Millimeters = u32; type Meters = u32;Dans le code final, on a accidentellement additionné des millimètres avec des mètres, ce qui a entraîné des erreurs de calcul. La typage n’a pas fonctionné — le compilateur n’a pas pu le détecter. On est passé au newtype et le problème a disparu.
Histoire
Dans une API ouverte, des alias étaient utilisés pour les identifiants de différentes entités (
type UserId = u64;,type OrderId = u64;). Une confusion est survenue : quelqu'un a interverti les paramètres, et un bug a été déployé en production à cause de l'impossibilité de les distinguer par leurs types.
Histoire
Un développeur a essayé d'implémenter un comportement unique du trait Display pour un alias de type :
type MyType = String; impl Display for MyType { ... }Le compilateur a renvoyé une erreur : "impossible d'implémenter un trait pour un alias de type". Le problème a été résolu via le newtype pattern.