Astuces Rust pour `match` : gérer les vecteurs selon leur longueur

Astuces Rust pour match : gérer les vecteurs selon leur longueur

Table des matières

La situation que vous avez déjà rencontrée

Vous écrivez une fonction Rust qui reçoit un Vec<T>. Et selon le nombre d’éléments (1, 2, 3 ou 4), vous devez exécuter un traitement différent : appeler d’autres fonctions, transmettre les valeurs à des handlers, etc.

En dehors de cette plage, c’est une erreur. Et quand on débute, on finit généralement avec ce genre de code :

 1fn process(vec: Vec<i32>) -> Result<(), String> {
 2    if vec.len() < 1 || vec.len() > 4 {
 3        return Err("Invalid number of elements".to_string());
 4    }
 5
 6    if vec.len() == 1 {
 7        func1(vec[0]);
 8    } else if vec.len() == 2 {
 9        func2(vec[0], vec[1]);
10    } else if vec.len() == 3 {
11        func3(vec[0], vec[1], vec[2]);
12    } else {
13        func4(vec[0], vec[1], vec[2], vec[3]);
14    }
15    Ok(())
16}

Ce code fonctionne, mais il n’est ni élégant, ni idiomatique. Chaque accès vec[i] suppose que l’indice est valide, et la logique devient vite bruyante.

Alors… comment faire mieux en Rust ?

Place à match — idiomatique, expressif et 100% sûr

Rust propose un outil puissant qui dépasse le simple “switch-case” : le pattern matching avec match.

Voici exactement la même logique, mais plus claire, plus sûre et beaucoup plus Rust :

 1fn process(vec: Vec<i32>) -> Result<(), String> {
 2    match vec.as_slice() {
 3        [a] => func1(*a),
 4        [a, b] => func2(*a, *b),
 5        [a, b, c] => func3(*a, *b, *c),
 6        [a, b, c, d] => func4(*a, *b, *c, *d),
 7        _ => return Err("Invalid number of elements".to_string()),
 8    }
 9    Ok(())
10}

Ce modèle est une démonstration parfaite du match sur slice et pattern matching sur vecteur en Rust.

D’un coup, l’intention est limpide. Et le compilateur vous protège contre toute erreur d’indexation.

Note

as_slice() renvoie &[T], donc chaque valeur est une référence. Vous pouvez aussi matcher directement les références :

1match vec.as_slice() {
2    [&a] => func1(a),
3    [&a, &b] => func2(a, b),
4    _ => ...
5}

Pourquoi cette approche est nettement supérieure

Voici les avantages principaux — tous valables dans des projets Rust réels :

  • ✔ Lisibilité immédiate: Chaque cas exprime exactement ce qu’il attend : [a], [a, b], [a, b, c]

  • ✔ Sécurité renforcée: Plus de risques de sortir des bornes avec vec[i].

  • ✔ Extensible sans douleur: Vous devez gérer un vecteur de 5 éléments ? Ajoutez un bras de match : c’est tout.

  • ✔ Idiomatique Rust: Ce style « sonne Rust » pour un Rustacean 1.

  • ✔ Aide du compilateur: Si vous oubliez un cas, Rust vous prévient. Aucun risque de logique incomplète.

  • ✔ Des noms explicites: Vous pouvez nommer correctement chaque valeur, et pas seulement vec[0]. Cette clarté supplémentaire joue beaucoup, surtout dans une logique plus complexe. Par example :

1match vec.as_slice() {
2    [x] => handle_single(x),
3    [x, y] => handle_pair(x, y),
4    [start, middle, end] => handle_triplet(start, middle, end),
5    _ => return Err("Invalid input".to_string()),
6}

Warning

Évitez vec[0] ou .get(0).unwrap() tant que la longueur n’est pas garantie. Avec le pattern matching, impossible d’oublier un cas critique.

Cas d’usage réels : tirer pleinement parti du pattern matching Rust

Le pattern matching sur slices et vecteurs n’est pas seulement élégant — c’est extrêmement puissant en pratique.

Voici des patterns utiles pour enrichir votre code Rust.

1. Ajouter des conditions avec des (if) guards

1match vec.as_slice() {
2    [a, b] if a == b => handle_equal_pair(a),
3    [a, b] => handle_unequal_pair(a, b),
4    _ => return Err("Unsupported input".to_string()),
5}

Vous gagnez une expressivité incroyable sans empiler les conditions.

2. Matching imbriqué sur les valeurs internes

Exemple typique avec des Option :

1match vec.as_slice() {
2    [Some(a), Some(b)] => handle_both_present(a, b),
3    [Some(a), None] => handle_partial(a),
4    _ => return Err("Invalid combination".to_string()),
5}

Très utile lorsqu’on manipule des enums imbriquées.

3. Match partiel avec “splat” (..)

1match vec.as_slice() {
2    [first, .., last] => handle_ends(first, last),
3    _ => return Err("Too short".to_string()),
4}

Parfait pour les algorithmes où seul le début et la fin comptent.

4. Modifier les valeurs sur place grâce à as_mut_slice()

1fn double_first(vec: &mut Vec<i32>) -> Result<(), String> {
2    match vec.as_mut_slice() {
3        [x, ..] => {
4            *x *= 2;
5            Ok(())
6        },
7        [] => Err("Empty vector".to_string()),
8    }
9}

Et pour plusieurs éléments :

1match vec.as_mut_slice() {
2    [a, b] => {
3        *a += 1;
4        *b *= 2;
5    },
6    _ => return Err("Expected exactly two items".to_string()),
7}

Idéal pour manipuler des données sans risque (évite les vec[0] += 1 susceptibles de paniquer).

5. Cas avancé : matcher des Rc

1use std::rc::Rc;
2
3match vec.as_slice() {
4    [a, b] if Rc::strong_count(a) > 1 => handle_shared(a, b),
5    [a, b] => handle_unique(a, b),
6    _ => return Err("Unexpected input".to_string()),
7}

Même avec des smart pointers, le pattern matching reste puissant.

Attention : déplacer la valeur hors d’un Rc est complexe — il faut souvent cloner ou restructurer.

Pourquoi éviter match vec.len() — même si ça semble logique

Beaucoup de développeurs venant de C/C++/Go/Java écrivent ceci instinctivement :

1match vec.len() {
2    1 => process1(vec[0]),
3    2 => process2(vec[0], vec[1]),
4    3 => process3(vec[0], vec[1], vec[1]), // ❌ erreur silencieuse
5    _ => return Err("Invalid input length".to_string()),
6}

Le problème ? Le compilateur ne peut rien vérifier pour vous.

  • Vous pouvez vous tromper d’indice (comme ci-dessus).
  • Impossible de nommer vos valeurs.
  • Aucune vérification de structure.
  • Aucune cohérence entre le match et l’accès.

Cette approche compile… mais elle est dangereuse.

Avec les slices, le compilateur vous protège.

Conclusion : utilisez match pour décrire la structure d’un vecteur

Quand votre logique dépend du nombre et de la structure des éléments, match avec des slices est l’outil Rust le plus propre, le plus sûr et le plus lisible.

Il évite les pièges liés à l’indexation, s’intègre naturellement avec les enums, les smart pointers, les mutations, et rend votre intention absolument claire.

Rust n’est pas qu’un langage sûr : c’est un langage qui pousse à écrire du code élégant et correct.

TL;DR

  • match + slice est la meilleure façon de gérer les vecteurs de longueur spécifique.
  • Code lisible, sûr, extensible et idiomatique.
  • Guard, patterns partiels, matching imbriqué → hyper puissant.
  • N’utilisez pas match vec.len() sauf cas très trivial.
  • Rust n’est pas seulement fait pour éviter les bugs — il est conçu pour favoriser la clarté.
  • Idéal pour tout ce qui concerne match sur longueur de vecteur, pattern matching sur slice, etc.

Vous débutez en Rust ? Découvrez cette sélection de ressources et conventions ici.


  1. Rustacean désigne quelqu’un maîtrisant Rust, comme « Pythonista » pour Python. ↩︎

Partager :

Articles Connexes

Enums vs Constantes : Pourquoi les Enums sont une solution plus sûre et plus intelligente

Enums vs Constantes : Pourquoi les Enums sont une solution plus sûre et plus intelligente

Utilisez-vous toujours des entiers ou des chaînes de caractères pour représenter des catégories fixes dans votre code ? Si c’est le cas, vous …

Lire la suite
Règles de Codage de Rust et Ressources Pédagogiques

Règles de Codage de Rust et Ressources Pédagogiques

Quel style de codage dois-je adopter pour mon code Rust afin d’assurer la cohérence avec mes bibliothèques Rust préférées ? Où puis-je apprendre …

Lire la suite
L'Enfer des Dépendances : 5 Stratégies pour Gérer les Risques Open Source (Le Dilemme de la Dépendance)

L’Enfer des Dépendances : 5 Stratégies pour Gérer les Risques Open Source (Le Dilemme de la Dépendance)

La question est : Faut-il importer ou non ? Une bibliothèque économise des semaines de codage, mais introduit des risques d’Enfer des …

Lire la suite