Entwurfsmuster in Rust mit Codebeispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten
Here is the translated HTML content in German: ```html

Entwurfsmuster in Rust: praktischer Leitfaden mit Beispielen



Entwurfsmuster sind bewährte Lösungen für typische Entwicklungsaufgaben. In Rust unterscheidet sich die Implementierung klassischer Muster aufgrund des einzigartigen Besitzsystems und Speichermodells oft von Sprachen mit Garbage Collection. In diesem Artikel werden wir drei Schlüsselmuster behandeln: Singleton, Builder und Strategy, angepasst an die Besonderheiten von Rust.



1. Singleton-Muster in Rust: globaler Zustand ohne unsafe



Singleton stellt sicher, dass eine Klasse nur eine Instanz hat, und bietet einen globalen Zugriffspunkt darauf. In Rust eignet sich dafür ideal die Crate once_cell oder das stabile std::sync::OnceLock (seit Rust 1.70).



Implementierung mit OnceLock

use std::sync::OnceLock;

pub struct Logger { level: String,}

impl Logger { pub fn global() -> &'static Logger { static INSTANCE: OnceLock<Logger> = OnceLock::new(); INSTANCE.get_or_init(|| Logger { level: "info".to_string(), }) }

pub fn log(&self, msg: &str) { println!("[{}] {}", self.level, msg); }}

fn main() { let logger = Logger::global(); logger.log("Hallo, Rust!"); // Logger::global() gibt dieselbe Instanz zurück}


Vorteile: Sicherheit (kein unsafe), faule Initialisierung, Threadsicherheit. Nachteil: globaler Zustand erschwert das Testen.



2. Builder-Muster: Konstruktion komplexer Objekte



Builder trennt die Konstruktion eines komplexen Objekts von seiner Darstellung. In Rust ist es besonders nützlich für Strukturen mit vielen optionalen Feldern.



Beispiel: Konfiguration eines HTTP-Clients

pub struct HttpClient {    url: String,    timeout: u64,    headers: Vec<(String, String)>,}

pub struct HttpClientBuilder { url: String, timeout: Option<u64>, headers: Vec<(String, String)>,}

impl HttpClientBuilder { pub fn new(url: &str) -> Self { HttpClientBuilder { url: url.to_string(), timeout: None, headers: vec![], } }

pub fn timeout(mut self, secs: u64) -> Self { self.timeout = Some(secs); self }

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

pub fn build(self) -> HttpClient { HttpClient { url: self.url, timeout: self.timeout.unwrap_or(30), headers: self.headers, } }}

fn main() { let client = HttpClientBuilder::new("https://api.example.com") .timeout(10) .header("Authorization", "Bearer token123") .build(); println!("Client-URL: {}", client.url);}


Wichtig: Die Verwendung von self (nicht &self) in den Methoden des Builders vermeidet Klonen und macht die API ergonomischer.



3. Strategy-Muster: Algorithmen als austauschbare Strategien



Strategy definiert eine Familie von Algorithmen, kapselt jeden einzelnen und macht sie austauschbar. In Rust wird dies durch Traits und dynamische Dispatch (Box<dyn Trait>) implementiert.



Beispiel: Sortiersystem

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

struct BubbleSort;impl SortStrategy for BubbleSort { fn sort(&self, data: &mut [i32]) { let n = data.len(); for i in 0..n { for j in 0..n - i - 1 { if data[j] > data[j + 1] { data.swap(j, j + 1); } } } }}

struct QuickSort;impl SortStrategy for QuickSort { fn sort(&self, da ```

Blogs

Buchempfehlungen