Tes propres invariants, déplacés de ta tête vers le type.Your own invariants, moved from your head into the type.
Option a montré le mouvement : prendre une règle qui vivait dans ta tête — « ça peut être absent » — et la déplacer dans le type, où le compilateur la fait respecter. Ce numéro généralise le geste à tes propres règles. Deux outils : le newtype, qui donne un sens et une garantie à un type primitif ; et le type-state, qui encode l'état d'un objet dans son type, pour que les opérations invalides n'existent tout simplement pas. La fin du Vol 3 : rendre l'illégal non pas détectable, mais impossible à écrire — dans ton domaine, pas seulement dans la bibliothèque standard.Option showed the move: take a rule that lived in your head — 'this can be absent' — and move it into the type, where the compiler enforces it. This issue generalizes the gesture to your own rules. Two tools: the newtype, which gives meaning and a guarantee to a primitive type; and type-state, which encodes an object's state into its type, so invalid operations simply don't exist. The close of Vol 3: making the illegal not detectable but impossible to write — in your domain, not just the standard library.
struct Email(String); // un newtype : un String, mais PAS un String impl Email { // le SEUL chemin de construction passe par la validation fn parse(s: String) -> Option<Email> { if s.contains('@') { Some(Email(s)) } else { None } } } // dès lors, un Email qui existe est un email validé — par construction. // et on ne peut plus le confondre avec un mot de passe, lui aussi String.
L'idée tient en une formule : ne valide pas une donnée pour la laisser ensuite circuler sous son type brut — transforme-la en un type qui prouve sa validité. Un String validé reste un String : trois fonctions plus loin, plus rien ne dit qu'il l'a été. Un Email issu de parse porte sa garantie partout où il va — la validation a eu lieu une fois, à la frontière, et le type s'en souvient pour toujours.The idea fits in a phrase: don't validate data only to let it flow on under its raw type — transform it into a type that proves its validity. A validated String is still a String: three functions later, nothing says it ever was. An Email from parse carries its guarantee everywhere it goes — validation happened once, at the boundary, and the type remembers it forever.
« Cette règle, je la vérifie à chaque usage, ou je l'encode une fois dans un type ? » Si une donnée a un invariant — validée, normalisée, dans une certaine unité, dans un certain état — un type dédié le porte une bonne fois. Le compilateur cesse alors de te faire confiance pour t'en souvenir, et le fait à ta place, à chaque appel.'This rule — do I check it at every use, or encode it once in a type?' If a piece of data has an invariant — validated, normalized, in a certain unit, in a certain state — a dedicated type carries it once and for all. The compiler then stops trusting you to remember it, and does so for you, at every call.
Un newtype comme Email(String) ne coûte rien à l'exécution : c'est exactement la représentation mémoire d'un String, l'enveloppe disparaît à la compilation. On parle d'abstraction « à coût nul » — un thème qui traversera tout le reste de la série. La sécurité de type est une affaire de compilation, pas de cycles CPU.A newtype like Email(String) costs nothing at runtime: it has exactly a String's memory representation, the wrapper vanishes at compile time. This is 'zero-cost' abstraction — a theme running through the rest of the series. Type safety is a compile-time matter, not CPU cycles.