ProgrammationDéveloppeur Backend

Comment les « alias de type » sont-ils implémentés en Rust, en quoi diffèrent-ils des nouveaux types (newtype pattern), et pourquoi est-il important de comprendre la différence lors de la conception d'une API ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

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.

  • Les alias sont pratiques pour la lisibilité ou les API généralisées.
  • Le newtype pattern assure la sécurité des types et permet d'implémenter des traits uniques pour de nouveaux types.

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.

Question piège.

Quelle est la différence entre un alias type et 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.

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


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.