Event Bus et pattern Observer en Rust : implémentation et exemples

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

Event Bus et pattern Observer en Rust : implémentation et exemples

L'Event Bus (bus d'événements) est un pattern architectural qui implémente le principe publish-subscribe (éditeur-abonné). En Rust, ce pattern est particulièrement utile pour construire des systèmes faiblement couplés où les composants communiquent via des événements sans se connaître directement. Dans cet article, nous allons voir comment créer votre propre Event Bus en utilisant le pattern Observer, et explorer les solutions existantes dans l'écosystème Rust.

Pattern Observer : les bases

Le pattern Observer définit une relation « un-à-plusieurs » entre objets. Lorsqu'un objet (l'éditeur) modifie son état, tous les objets dépendants (les abonnés) sont automatiquement notifiés. En Rust, cela se réalise souvent via des traits et la dispatch dynamique.

Implémentation basique d'Observer en Rust

use std::collections::HashMap;

// Trait pour l'observateur
trait Observer {
    fn update(&self, event: &str);
}

// Structure de l'éditeur
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);
        }
    }
}

// Observateur concret
struct Logger;

impl Observer for Logger {
    fn update(&self, event: &str) {
        println!("[Logger] Événement reçu : {}", event);
    }
}

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

Event Bus : version avancée d'Observer

L'Event Bus est un bus centralisé qui achemine les événements par type. Contrairement à un simple Observer, il prend en charge différents types d'événements et le filtrage des abonnements.

Implémentation d'un Event Bus avec types d'événements

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

// Type d'événement
#[derive(Clone, Debug)]
enum EventType {
    UserLogin,
    UserLogout,
    DataUpdate,
}

// Structure d'événement
#[derive(Clone, Debug)]
struct Event {
    event_type: EventType,
    payload: String,
}

// Gestionnaire d'événement
type Handler = Arc<dyn Fn(Event) + Send + Sync>;

// Bus d'événements
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!("Utilisateur connecté : {}", event.payload);
    });

    bus.subscribe(EventType::UserLogin, |event| {
        // Autre gestionnaire
        println!("Connexion enregistrée : {}", event.payload);
    });

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

Event Bus asynchrone avec Tokio

Dans les applications réelles, le traitement asynchrone des événements est souvent nécessaire. La bibliothèque tokio avec ses canaux broadcast est parfaitement adaptée.

Utilisation de tokio::sync::broadcast

use tokio::sync::broadcast;

#[tokio::main]
async fn main() {
    // Création d'un canal broadcast avec un buffer de 16 messages
    let (tx, mut rx1) = broadcast::channel::<String>(16);
    let mut rx                    

Recommendations