Un atelier Rust · Édition d'ApprentissageA Rust Workshop · Learning Edition

Traits & generics, ou le polymorphisme à coût zéro Traits & generics, or zero-cost polymorphism

La monomorphisation : zéro coût à l'exécution, mais du code dupliqué à la compilation. Rust n'a pas d'héritage — et c'est un choix, pas un manque. Monomorphization: zero runtime cost, but duplicated code at compile time. Rust has no inheritance — and that's a choice, not a lack.

AudienceAudience
Dev à l'aise avec les types et les enums (Vol 3–4) Dev comfortable with types and enums (Vol 3–4)
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●●○ TraitsGenericsMonomorphisation

Chapitre 1 en accès libre — la suite (ch. 2 à 5) est réservée. Chapter 1 free to read — the rest (ch. 2–5) is members-only.

01CadrageFraming3 min

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.

Le trait et ses impl — un contrat, des types qui y souscriventThe trait and its impls — a contract, types that subscribe to it
// 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());
}
Le contrat, pas la parentéThe contract, not the lineage

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.

Le réflexe du numéroThe issue's reflex

« 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.

Tu connais déjà les traits.You already know traits.

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.

🔒

La suite est réservée The rest is members-only

Le premier numéro est libre. Débloque tout The Rust Loop — tous les volumes, à vie — pour 5 €, paiement unique. The first issue is free. Unlock all of The Rust Loop — every volume, forever — for €5, one-time.

Retour au kiosqueBack to newsstand