Modèles de conception en Rust : exemples et implémentation

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Patrons de conception en Rust avec exemples



Les patrons de conception sont des solutions éprouvées aux problèmes courants du développement logiciel. En Rust, grâce à son système unique de possession, d'emprunt et de durée de vie, l'implémentation des patrons GoF classiques nécessite souvent une approche particulière. Dans cet article, nous analyserons plusieurs patrons populaires à l'aide d'exemples de code Rust réel.



Pourquoi Rust change-t-il l'approche des patrons ?

Contrairement aux langages avec ramasse-miettes (Java, C#), Rust impose des règles strictes sur la gestion de la mémoire. Cela rend certaines implémentations classiques (par exemple, Singleton via un état mutable global) dangereuses ou inefficaces. Cependant, Rust propose ses propres alternatives élégantes, basées sur les types et les traits.



1. Builder — construction pas à pas des objets

Builder est l'un des patrons les plus utilisés en Rust. Il est idéal pour configurer des structures complexes, surtout lorsqu'il y a de nombreux champs optionnels.



Exemple : création d'une requête HTTP

struct Request {    method: String,    url: String,    headers: Vec<(String, String)>,    body: Option<String>,}

struct RequestBuilder { method: String, url: String, headers: Vec<(String, String)>, body: Option<String>,}

impl RequestBuilder { fn new(method: &str, url: &str) -> Self { Self { method: method.to_string(), url: url.to_string(), headers: Vec::new(), body: None, } }

fn header(mut self, key: &str, value: &str) -> Self { self.headers.push((key.to_string(), value.to_string())); self }

fn body(mut self, body: &str) -> Self { self.body = Some(body.to_string()); self }

fn build(self) -> Request { Request { method: self.method, url: self.url, headers: self.headers, body: self.body, } }}

fn main() { let req = RequestBuilder::new("POST", "https://api.example.com/data") .header("Content-Type", "application/json") .header("Authorization", "Bearer token123") .body(r#"{"key": "value"}"#) .build();

println!("URL: {}", req.url); println!("Headers count: {}", req.headers.len());}


Avantages de l'approche en Rust

  • Sécurité : l'objet n'est créé qu'après l'appel à build() — une configuration incomplète est impossible.
  • Lisibilité : la chaîne de méthodes est intuitivement compréhensible.
  • Sécurité des types : le compilateur vérifie les types à chaque étape.


2. Strategy — choix d'algorithme à la volée

Strategy permet de modifier le comportement d'un objet à l'exécution. En Rust, cela s'implémente via les traits et la dispatch dynamique (Box avec traits) ou statique (génériques).



Exemple : tri de données

trait SortStrategy {    fn sort(&self, data: &mut [i32]);}

struct BubbleSort;struct QuickSort;

impl SortStrategy for BubbleSort { fn sort(&self, data: &mut [i32]) { // Implémentation très simple du tri à bulles for i in 0..data.len() { for j in 0..data.len() - 1 - i { if data[j] > data[j + 1] { data.swap(j, j + 1); } } } }}

impl SortStrategy for QuickSort { fn sort(&self, data: &mut [i32]) { // Tri rapide (simplifié) if data.len() > 1 { let pivot = data.len() / 2; data.sort(); // Utilisation de la fonction intégrée pour l'exemple } }}

struct Sorter { strategy: Box<dyn SortStrategy>,}

impl Sorter { fn new(strategy: Box<dyn SortStrategy>) ->

Blogs

Book Recommendations