Pas d'héritage. Un contrat que le type choisit d'honorer.No inheritance. A contract the type chooses to honor.
Les quatre premiers volumes portaient sur la sûreté ; celui-ci ouvre la réutilisation. La question change : comment écrire un comportement une seule fois et l'appliquer à des types différents ? Les langages à objets répondent par l'héritage — un type dérive d'un autre et récupère ses méthodes. Rust n'a pas d'héritage, et ce n'est pas un manque : c'est un choix. Le partage de comportement y passe par les traits — des contrats. Un trait déclare ce qu'un type doit savoir faire ; un type décide, par un bloc impl, d'honorer ce contrat. Une fonction générique n'exige alors plus un type précis, mais la preuve qu'il respecte le contrat. C'est le polymorphisme sans la hiérarchie.The first four volumes were about safety; this one opens reuse. The question shifts: how do you write a behavior once and apply it to different types? Object-oriented languages answer with inheritance — a type derives from another and inherits its methods. Rust has no inheritance, and that's not a lack: it's a choice. Behavior sharing goes through traits — contracts. A trait declares what a type must know how to do; a type decides, via an impl block, to honor that contract. A generic function then no longer demands a precise type, but proof that it respects the contract. It's polymorphism without the hierarchy.
// Un trait : un CONTRAT de comportement, que chaque type choisit d'honorer trait Surface { fn aire(&self) -> f64; // « quiconque est Surface sait donner son aire » } impl Surface for Cercle { // le cercle décide d'honorer le contrat fn aire(&self) -> f64 { std::f64::consts::PI * self.r * self.r } } impl Surface for Rectangle { fn aire(&self) -> f64 { self.l * self.h } } // une fonction générique exige le contrat, pas un type précis : fn afficher_aire<F: Surface>(forme: &F) { // « tout F qui est Surface » println!("aire = {}", forme.aire()); }
La différence avec l'héritage est profonde. Cercle et Rectangle ne descendent d'aucune classe Forme ; ils n'ont rien en commun structurellement. Ils partagent seulement de savoir faire la même chose — donner leur aire. Le trait capture exactement ce partage, et rien de plus : pas de champs hérités, pas de constructeur de base, pas de chaîne de parenté à remonter. On peut même implémenter un trait pour un type qu'on n'a pas écrit (sous conditions — c'est le sujet du n°3). Le comportement se compose par capacités, là où l'héritage l'impose par filiation.The difference from inheritance runs deep. Cercle and Rectangle descend from no Shape class; they share nothing structurally. They only share knowing how to do the same thing — give their area. The trait captures exactly that sharing, and nothing more: no inherited fields, no base constructor, no lineage to climb. You can even implement a trait for a type you didn't write (under conditions — issue n°3's subject). Behavior composes by capability, where inheritance imposes it by descent.
« Ce code a-t-il besoin d'un type précis, ou d'une capacité ? » S'il ne fait qu'appeler .aire(), il n'a que faire de savoir si c'est un cercle : il lui faut « quelque chose qui sait donner son aire ». On l'écrit générique, borné par le trait — une fois, pour tous les types présents et futurs qui honoreront le contrat.'Does this code need a precise type, or a capability?' If it only calls .aire(), it has no use for knowing it's a circle: it needs 'something that can give its area'. You write it generic, bounded by the trait — once, for all present and future types that will honor the contract.
Toute la série en a croisé : Display formatait nos messages, Error structurait nos pannes, From convertissait nos erreurs sous le ?, PartialEq permettait le ==. Ce ne sont pas des mécanismes du langage à part : ce sont des traits de la bibliothèque standard, exactement de la forme qu'on dessine ici. Ce numéro nomme et généralise ce que tu utilisais déjà sans le savoir.The whole series has met some: Display formatted our messages, Error structured our failures, From converted our errors under the ?, PartialEq enabled ==. These aren't separate language mechanisms: they're standard-library traits, exactly the shape we draw here. This issue names and generalizes what you were already using without knowing it.