Event Bus и паттерн Observer в Rust: реализация и примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Event Bus и паттерн Observer в Rust: реализация и примеры

Event Bus (шина событий) — это архитектурный паттерн, реализующий принцип publish-subscribe (издатель-подписчик). В Rust этот паттерн особенно полезен для построения слабосвязанных систем, где компоненты общаются через события, не зная друг о друге напрямую. В этой статье мы разберём, как создать собственный Event Bus с использованием паттерна Observer, и рассмотрим готовые решения из экосистемы Rust.

Паттерн Observer: основы

Паттерн Observer определяет отношение «один-ко-многим» между объектами. Когда один объект (издатель) изменяет своё состояние, все зависимые объекты (подписчики) автоматически уведомляются. В Rust это часто реализуется через трейты и динамическую диспетчеризацию.

Базовая реализация Observer в Rust

use std::collections::HashMap;

// Трейт для наблюдателя
trait Observer {
    fn update(&self, event: &str);
}

// Структура издателя
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);
        }
    }
}

// Конкретный наблюдатель
struct Logger;

impl Observer for Logger {
    fn update(&self, event: &str) {
        println!("[Logger] Получено событие: {}", event);
    }
}

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

Event Bus: расширенная версия Observer

Event Bus — это централизованная шина, которая маршрутизирует события по типам. В отличие от простого Observer, здесь поддерживаются разные типы событий и фильтрация подписок.

Реализация Event Bus с типами событий

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

// Тип события
#[derive(Clone, Debug)]
enum EventType {
    UserLogin,
    UserLogout,
    DataUpdate,
}

// Структура события
#[derive(Clone, Debug)]
struct Event {
    event_type: EventType,
    payload: String,
}

// Обработчик события
type Handler = Arc<dyn Fn(Event) + Send + Sync>;

// Шина событий
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!("Пользователь вошёл: {}", event.payload);
    });

    bus.subscribe(EventType::UserLogin, |event| {
        // Другой обработчик
        println!("Логин зафиксирован: {}", event.payload);
    });

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

Асинхронный Event Bus с Tokio

В реальных приложениях часто требуется асинхронная обработка событий. Для этого отлично подходит библиотека tokio с её каналами broadcast.

Использование tokio::sync::broadcast

use tokio::sync::broadcast;

#[tokio::main]
async fn main() {
    // Создаём канал broadcast с буфером на 16 сообщений
    let (tx, mut rx1) = broadcast::channel::<String>(16);
    let mut rx2 = tx.subscribe();

    // Первый подписчик
    tokio::spawn(async move {
        while let Ok(msg) = rx1.recv().await {
            println!("Подписчик 1 получил: {}", msg);
        }
    });

    // Второй подписчик
    tokio::spawn(async move {
        while let Ok(msg) = rx2.recv().await {
            println!("Подписчик 2 получил: {}", msg);
        }
    });

    // Публикуем события
    tx.send("Событие 1".to_string()).unwrap();
    tx.send("Событие 2".to_string()).unwrap();

    // Даём время на обработку
    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}

Готовые библиотеки для Event Bus в Rust

Вместо написания собственной реализации можно использовать проверенные библиотеки

Рекомендаций