Event Bus and Observer Pattern in Rust: Implementation and Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Event Bus and Observer Pattern in Rust: Implementation and Examples



Event Bus is an architectural pattern that implements the publish-subscribe principle. In Rust, this pattern is especially useful for building loosely coupled systems where components communicate through events without knowing about each other directly. In this article, we will analyze how to create your own Event Bus using the Observer pattern and consider ready-made solutions from the Rust ecosystem.



Observer Pattern: Basics



The Observer pattern defines a one-to-many relationship between objects. When one object (publisher) changes its state, all dependent objects (subscribers) are automatically notified. In Rust, this is often implemented through traits and dynamic dispatch.



Basic Observer Implementation in Rust



use std::collections::HashMap;

// Trait for observertrait Observer { fn update(&self, event: &str);}

// Publisher structurestruct 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); } }}

// Concrete observerstruct Logger;

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

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


Event Bus: Extended Version of Observer



Event Bus is a centralized bus that routes events by type. Unlike a simple Observer, it supports different event types and subscription filtering.



Event Bus Implementation with Event Types



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

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

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

// Event handlertype Handler = Arc<dyn Fn(Event) + Send + Sync>;

// Event busstruct 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!("User logged in: {}", event.payload); });

bus.subscribe(EventType::UserLogin, |event| { // Another handler println!("Login recorded: {}", event.payload); });

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


Asynchronous Event Bus with Tokio



In real applications, asynchronous event processing is often required. The tokio library with its broadcast channels is perfect for this.



Using tokio::sync::broadcast



use tokio::sync::broadcast;

#[tokio::main]async fn m

Recommendations