---
title: "Les supertraits : imposer une hiérarchie de comportements"
description: "Exploiter les supertraits pour établir des hiérarchies de comportements et les combiner avec des clauses where pour optimiser des algorithmes génériques complexes pour la performance et la sécurité de type"
url: "https://mayorana.ch/fr/blog/supertraits-bounds-optimization"
language: "fr"
date: "2025-12-02"
author: "Mohamed Bennekrouf"
tags:
  - "rust"
  - "supertraits"
  - "bounds"
  - "generics"
  - "optimization"
  - "traits"
en_version: "https://mayorana.ch/en/blog/supertraits-bounds-optimization.md"
---

# Les supertraits : imposer une hiérarchie de comportements

Dans une bibliothèque de calcul numérique Rust, j'utiliserais les supertraits pour créer une hiérarchie de comportements, m'assurant que les opérations avancées s'appuient sur les basiques, et les combiner avec des clauses where pour écrire un algorithme générique complexe qui soit type-safe et performant. Cette approche organise le code logiquement, impose la justesse pendant la compilation, et optimise pour l'efficacité via le [dispatch statique](https://mayorana.ch/fr/blog/dispatch-performance-rust).

<div class="svg-container" style="margin:2rem 0;">
<svg class="td5-fig" viewBox="0 0 800 300" width="100%" style="height:auto;max-width:780px;display:block;margin:0 auto;" role="img" aria-label="Hiérarchie de supertraits en couches, de Numeric jusqu'à AdvancedNumeric consommée par matrix_multiply">
<style>
.td5-fig{--bg:#f8fafc;--box:#ffffff;--tx:#1e293b;--mut:#64748b;--ln:#cbd5e1;--ac:#FF6B00}
:root.dark .td5-fig,[data-theme="dark"] .td5-fig{--bg:#0f172a;--box:#1e293b;--tx:#f8fafc;--mut:#94a3b8;--ln:#475569}
.td5-fig .box{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.td5-fig .boxAc{fill:var(--box);stroke:var(--ac);stroke-width:2}
.td5-fig .tx{fill:var(--tx);font:600 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5-fig .ti{fill:var(--tx);font:700 14px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5-fig .mut{fill:var(--mut);font:600 11px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5-fig .ln{stroke:var(--ln);stroke-width:1.5;fill:none}
.td5-fig .lnAc{stroke:var(--ac);stroke-width:2;fill:none}
</style>
<!-- markers -->
<defs>
<marker id="td5-arrow-fr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0L10,5L0,10z" fill="var(--ln)"/></marker>
<marker id="td5-arrowAc-fr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0L10,5L0,10z" fill="var(--ac)"/></marker>
</defs>
<!-- base layer: Numeric -->
<rect class="box" x="220" y="200" width="360" height="56" rx="6"/>
<text x="400" y="222" class="tx">trait Numeric: Add + Copy</text>
<text x="400" y="240" class="mut">fn zero() -&gt; Self</text>
<!-- arrow up to AdvancedNumeric -->
<path class="lnAc" d="M400,200 L400,156" marker-end="url(#td5-arrowAc-fr)"/>
<!-- advanced layer -->
<rect class="boxAc" x="220" y="100" width="360" height="56" rx="6"/>
<text x="400" y="122" class="tx">trait AdvancedNumeric: Numeric + Mul</text>
<text x="400" y="140" class="mut">fn one() -&gt; Self</text>
<!-- arrow up to matrix_multiply -->
<path class="ln" d="M400,100 L400,56" marker-end="url(#td5-arrow-fr)"/>
<!-- consumer -->
<rect class="box" x="180" y="20" width="440" height="36" rx="6"/>
<text x="400" y="43" class="tx">matrix_multiply&lt;T: AdvancedNumeric&gt;(...)</text>
<!-- captions -->
<text x="400" y="278" class="mut">opérations basiques : + et zero()</text>
<text x="120" y="128" class="mut">supertrait</text>
<text x="400" y="295" class="mut">Advanced exige Numeric — pas de + veut dire pas de *, imposé pendant la compilation</text>
</svg>
</div>

## Concevoir la Hiérarchie de Traits

Pour les types numériques, je définirais une hiérarchie de traits :

```rust
use std::ops::{Add, Mul};

// Opérations basiques que tout type numérique doit supporter
trait Numeric: Add<Self, Output = Self> + Copy {
    fn zero() -> Self;
}

// Opérations avancées pour les types supportant la multiplication
trait AdvancedNumeric: Numeric + Mul<Self, Output = Self> {
    fn one() -> Self;
}
```

**Supertrait** : `AdvancedNumeric: Numeric` signifie que tout type implémentant `AdvancedNumeric` doit aussi implémenter `Numeric`. Cela impose que les types avancés (avec `*` et `one`) aient les opérations basiques (`+` et `zero`).

**Pourquoi** : Organise les comportements hiérarchiquement—les ops basiques sont fondamentales, les ops avancées s'appuient dessus, reflétant la structure mathématique.

## Exemple : multiplication de matrices générique

J'écrirais un algorithme de multiplication de matrices générique utilisant ces traits :

```rust
fn matrix_multiply<T>(a: &[T], b: &[T], rows_a: usize, cols_a: usize, cols_b: usize) -> Vec<T>
where
    T: AdvancedNumeric,
    T::Output: Into<f64>, // Pour du debugging potentiel ou mise à l'échelle
{
    let mut result = vec![T::zero(); rows_a * cols_b];
    for i in 0..rows_a {
        for j in 0..cols_b {
            let mut sum = T::zero();
            for k in 0..cols_a {
                sum = sum + a[i * cols_a + k] * b[k * cols_b + j];
            }
            result[i * cols_b + j] = sum;
        }
    }
    result
}

// Implémentations
impl Numeric for f32 {
    fn zero() -> Self { 0.0 }
}
impl AdvancedNumeric for f32 {
    fn one() -> Self { 1.0 }
}
impl Numeric for i32 {
    fn zero() -> Self { 0 }
}
impl AdvancedNumeric for i32 {
    fn one() -> Self { 1 }
}

// Usage
let a = vec![1.0_f32, 2.0, 3.0, 4.0]; // matrice 2x2
let b = vec![5.0_f32, 6.0, 7.0, 8.0]; // matrice 2x2
let result = matrix_multiply(&a, &b, 2, 2, 2); // [[19, 22], [43, 50]]
```

## Comment les Supertraits et les Clauses where Améliorent la conception

### Organisation du Code
- **Supertraits** : `AdvancedNumeric: Numeric` crée une hiérarchie claire. Les ops basiques (`+`, `zero`) sont universelles ; les ops avancées (`*`, `one`) sont pour les types spécialisés. Cela reflète les maths : tous les nombres s'additionnent, mais ne se multiplient pas tous (ex : quaternions vs matrices).
- **Modularité** : De nouveaux traits (ex : `ComplexNumeric`) peuvent étendre `AdvancedNumeric`, réutilisant le comportement existant.

### Sécurité de Type
- **Supertraits** : S'assurent que `matrix_multiply` n'accepte que les types avec `Add` et `Mul` via `AdvancedNumeric`. Sans `Numeric`, un type pourrait implémenter `Mul` mais pas `Add`, cassant l'algorithme.
- **Clauses Where** : `T: AdvancedNumeric` est concis, regroupant plusieurs contraintes. `T::Output: Into<f64>` ajoute de la flexibilité pour le debugging sans encombrer la signature.
- **Vérifications pendant la compilation** : Les types invalides (ex : `String`) échouent tôt :

```rust
let strings = vec!["a", "b"];
matrix_multiply(&strings, &strings, 1, 1, 1); // Erreur : String manque Numeric
```

<div class="svg-container" style="margin:2rem 0;">
<svg class="td5b-fig" viewBox="0 0 800 250" width="100%" style="height:auto;max-width:780px;display:block;margin:0 auto;" role="img" aria-label="Tableau des obligations montrant quelles exigences de la chaîne Numeric et AdvancedNumeric sont satisfaites par f32, i32 et str">
<style>
.td5b-fig{--bg:#f8fafc;--box:#ffffff;--tx:#1e293b;--mut:#64748b;--ln:#cbd5e1;--ac:#FF6B00}
:root.dark .td5b-fig,[data-theme="dark"] .td5b-fig{--bg:#0f172a;--box:#1e293b;--tx:#f8fafc;--mut:#94a3b8;--ln:#475569}
.td5b-fig .box{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.td5b-fig .boxAc{fill:var(--box);stroke:var(--ac);stroke-width:2}
.td5b-fig .tx{fill:var(--tx);font:600 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5b-fig .ti{fill:var(--tx);font:700 14px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5b-fig .mut{fill:var(--mut);font:600 11px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5b-fig .ok{fill:var(--tx);font:700 13px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5b-fig .no{fill:var(--ac);font:700 13px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.td5b-fig .ln{stroke:var(--ln);stroke-width:1.5;fill:none}
</style>
<!-- title -->
<text x="400" y="26" class="ti">Ce qu'un seul bound T: AdvancedNumeric exige vraiment</text>
<!-- group labels -->
<text x="317" y="46" class="mut">hérité de Numeric</text>
<text x="555" y="46" class="mut">ajouté par AdvancedNumeric</text>
<!-- header row -->
<rect class="box" x="30" y="54" width="750" height="30" rx="4"/>
<text x="100" y="73" class="mut">type</text>
<text x="222" y="73" class="mut">Add&lt;Out=Self&gt;</text>
<text x="317" y="73" class="mut">Copy</text>
<text x="412" y="73" class="mut">zero()</text>
<text x="507" y="73" class="mut">Mul&lt;Out=Self&gt;</text>
<text x="602" y="73" class="mut">one()</text>
<text x="715" y="73" class="mut">verdict</text>
<!-- row f32 -->
<rect class="box" x="30" y="84" width="750" height="36" rx="4"/>
<text x="100" y="106" class="tx">f32</text>
<text x="222" y="107" class="ok">✓</text>
<text x="317" y="107" class="ok">✓</text>
<text x="412" y="107" class="ok">✓</text>
<text x="507" y="107" class="ok">✓</text>
<text x="602" y="107" class="ok">✓</text>
<text x="715" y="107" class="mut">monomorphisé</text>
<!-- row i32 -->
<rect class="box" x="30" y="120" width="750" height="36" rx="4"/>
<text x="100" y="142" class="tx">i32</text>
<text x="222" y="143" class="ok">✓</text>
<text x="317" y="143" class="ok">✓</text>
<text x="412" y="143" class="ok">✓</text>
<text x="507" y="143" class="ok">✓</text>
<text x="602" y="143" class="ok">✓</text>
<text x="715" y="143" class="mut">monomorphisé</text>
<!-- row &str -->
<rect class="boxAc" x="30" y="156" width="750" height="36" rx="4"/>
<text x="100" y="178" class="tx">&amp;str</text>
<text x="222" y="179" class="no">✗</text>
<text x="317" y="179" class="ok">✓</text>
<text x="412" y="179" class="no">✗</text>
<text x="507" y="179" class="no">✗</text>
<text x="602" y="179" class="no">✗</text>
<text x="715" y="179" class="mut">error[E0277]</text>
<!-- column separators -->
<path class="ln" d="M175,54 L175,192"/>
<path class="ln" d="M270,54 L270,192"/>
<path class="ln" d="M365,54 L365,192"/>
<path class="ln" d="M460,54 L460,192"/>
<path class="ln" d="M555,54 L555,192"/>
<path class="ln" d="M650,54 L650,192"/>
<!-- caption -->
<text x="400" y="222" class="mut">Copy ne suffit pas : une seule case manquante et matrix_multiply n'est jamais instancié pour ce type</text>
</svg>
</div>

### Efficacité
- **Dispatch Statique** : `T: AdvancedNumeric` déclenche la monomorphization, générant du code spécialisé pour `f32`, `i32`, etc. Les opérations comme `+` et `*` s'inlinent vers des instructions natives (ex : `fadd` pour `f32`).
- **Bounds Minimaux** : `Copy` évite le clonage, `Output = Self` assure pas de conversions de type dans le chemin chaud. `Into<f64>` n'est utilisé que si nécessaire, souvent optimisé.
- **Pas d'Overhead** : La hiérarchie n'ajoute aucun coût à l'exécution—les supertraits sont des contraintes pendant la compilation.

## Rôle des Clauses where

- **Clarté** : Sortent les bounds complexes (`T: AdvancedNumeric`, `T::Output: Into<f64>`) de la signature de fonction, améliorant la lisibilité.
- **Flexibilité** : Permettent des contraintes supplémentaires sans altérer la hiérarchie de traits (ex : ajouter `T: Debug` pour le logging).
- **Optimisation** : Permettent au compilateur de voir toutes les contraintes en amont, aidant l'inlining et les optimisations de boucle (ex : [SIMD](https://mayorana.ch/fr/blog/simd-matrix-mult-rust) pour les tableaux `f32`).

## Exemple d'Optimisation

Pour `f32`, la boucle interne pourrait compiler vers :

```asm
; Pseudocode
xorps xmm0, xmm0   ; sum = 0.0
loop:
  movss xmm1, [rsi] ; a[i * cols_a + k]
  mulss xmm1, [rdi] ; * b[k * cols_b + j]
  addss xmm0, xmm1  ; sum += ...
  add rsi, 4
  dec rcx
  jnz loop
```

**Pourquoi** : `AdvancedNumeric` assure `Add` et `Mul`, inlinés comme `addss` et `mulss`. La monomorphization adapte cela à `f32`.

## Compromis

- **Taille de Code** : La monomorphization crée une version par `T` (ex : `f32`, `i32`), augmentant la taille du binaire. Atténué en limitant les types supportés ou utilisant `dyn AdvancedNumeric` pour les chemins froids.
- **Complexité** : Les supertraits ajoutent un overhead de conception mais clarifient l'intention vs des bounds plats (ex : `T: Add + Mul + Copy`).

## Vérification

### Tests
Valide la justesse :

```rust
let a = vec![1.0_f32, 2.0, 3.0, 4.0];
let b = vec![5.0_f32, 6.0, 7.0, 8.0];
let result = matrix_multiply(&a, &b, 2, 2, 2);
assert_eq!(result, vec![19.0, 22.0, 43.0, 50.0]);
```

### Benchmark
Utilise criterion :

```rust
use criterion::{black_box, Criterion};
fn bench(c: &mut Criterion) {
    let a = vec![1.0_f32; 16];
    let b = vec![2.0_f32; 16];
    c.bench_function("matrix_multiply", |b| b.iter(|| matrix_multiply(black_box(&a), black_box(&b), 4, 4, 4)));
}
```

Attends-toi à des performances serrées grâce à l'inlining.

### Assembleur
`cargo rustc --release -- --emit asm` confirme les ops natives.

## Où les supertraits gagnent leur place
J'utiliserais les supertraits (`AdvancedNumeric: Numeric`) pour structurer une bibliothèque numérique, m'assurant que `matrix_multiply` obtient les ops basiques et avancées, avec des clauses where ajoutant flexibilité et clarté. Cela impose la sécurité, organise le code, et optimise via le dispatch statique, idéal pour la performance.
