Event Bus und Observer-Pattern in Rust: Implementierung & Beispiele

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

Event Bus und Observer-Pattern in Rust: Implementierung und Beispiele

Ein Event Bus ist ein Architekturmuster, das das Publish-Subscribe-Prinzip (Verleger-Abonnent) umsetzt. In Rust ist dieses Muster besonders nützlich, um lose gekoppelte Systeme zu bauen, bei denen Komponenten über Ereignisse kommunizieren, ohne sich direkt zu kennen. In diesem Artikel zeigen wir, wie du einen eigenen Event Bus mit dem Observer-Pattern erstellst, und betrachten fertige Lösungen aus dem Rust-Ökosystem.

Observer-Pattern: Grundlagen

Das Observer-Pattern definiert eine Eins-zu-Viele-Beziehung zwischen Objekten. Wenn ein Objekt (der Verleger) seinen Zustand ändert, werden alle abhängigen Objekte (die Abonnenten) automatisch benachrichtigt. In Rust wird dies oft über Traits und dynamische Dispatch realisiert.

Basisimplementierung des Observer-Patterns in Rust

use std::collections::HashMap;

// Trait für den Beobachter
trait Observer {
    fn update(&self, event: &str);
}

// Struktur des Verlegers
struct EventManager {
    observers: Vec<Box<dyn Observer>>,
}

impl EventManager {
    fn new() -> Self {
        EventManager {
            observers: Vec::new(),
        }
    }

    fn subscribe(&mut self, observer: Box<dyn Observer>) {
        self.observers.push(observer);
    }

    fn notify(&self, event: &str) {
        for observer in &self.observers {
            observer.update(event);
        }
    }
}

// Konkreter Beobachter
struct Logger;

impl Observer for Logger {
    fn update(&self, event: &str) {
        println!("[Logger] Ereignis empfangen: {}", event);
    }
}

fn main() {
    let mut manager = EventManager::new();
    manager.subscribe(Box::new(Logger));
    manager.notify("user.login");
}

Event Bus: Erweiterte Version des Observer-Patterns

Ein Event Bus ist eine zentrale Schnittstelle, die Ereignisse nach Typen routet. Im Gegensatz zum einfachen Observer werden hier verschiedene Ereignistypen und gefilterte Abonnements unterstützt.

Implementierung eines Event Bus mit Ereignistypen

use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

// Ereignistyp
#[derive(Clone, Debug)]
enum EventType {
    UserLogin,
    UserLogout,
    DataUpdate,
}

// Ereignisstruktur
#[derive(Clone, Debug)]
struct Event {
    event_type: EventType,
    payload: String,
}

// Ereignis-Handler
type Handler = Arc<dyn Fn(Event) + Send + Sync>;

// Event Bus
struct EventBus {
    handlers: HashMap<EventType, Vec<Handler>>,
}

impl EventBus {
    fn new() -> Self {
        EventBus {
            handlers: HashMap::new(),
        }
    }

    fn subscribe<F>(&mut self, event_type: EventType, handler: F)
    where
        F: Fn(Event) + 'static + Send + Sync,
    {
        self.handlers
            .entry(event_type)
            .or_insert_with(Vec::new)
            .push(Arc::new(handler));
    }

    fn publish(&self, event: Event) {
        if let Some(handlers) = self.handlers.get(&event.event_type) {
            for handler in handlers {
                handler(event.clone());
            }
        }
    }
}

fn main() {
    let mut bus = EventBus::new();

    bus.subscribe(EventType::UserLogin, |event| {
        println!("Benutzer angemeldet: {}", event.payload);
    });

    bus.subscribe(EventType::UserLogin, |event| {
        // Ein weiterer Handler
        println!("Login protokolliert: {}", event.payload);
    });

    bus.publish(Event {
        event_type: EventType::UserLogin,
        payload: "user123".to_string(),
    });
}

Asynchroner Event Bus mit Tokio

In echten Anwendungen ist oft eine asynchrone Ereignisverarbeitung erforderlich. Hierfür eignet sich die Bibliothek tokio mit ihren broadcast-Kanälen hervorragend.

Verwendung von tokio::sync::broadcast

use tokio::sync::broadcast;

#[tokio::main]
async fn main() {
    // Erstelle einen broadcast-Kanal mit einem Puffer von 16 Nachrichten
    let (tx, mut rx1) = broadcast::channel::<String>(16);
    let mut rx                    

Empfehlungen