如何在Rust中实现支持动态增删订阅者的同步发布订阅系统?
在Rust中实现单线程同步发布订阅系统
你的核心问题在于裸引用无法表达异构生命周期的集合,Rust的类型系统要求Vec中的元素必须是同一种类型(包括生命周期参数),而你需要的是让EventRouter持有不同生命周期的监听器,同时保证这些监听器在被引用期间有效。下面是几种符合单线程同步约束的可行方案:
方案1:用Rc共享所有权(最直观的实现)
单线程场景下,Rc可以安全地共享对象所有权,绕开生命周期的静态检查,通过引用计数管理对象的存活时间。
use std::rc::Rc; struct EventListener {} impl EventListener { fn on_event(&self, event: &str) { println!("{}", event); } } struct EventRouter { listeners: Vec<Rc<EventListener>>, } impl EventRouter { fn new() -> Self { Self { listeners: Vec::new() } } fn add_listener(&mut self, listener: Rc<EventListener>) { self.listeners.push(listener); } // 支持按索引移除监听器,若需要精准移除可给Listener加唯一ID,改用HashMap存储 fn remove_listener(&mut self, index: usize) -> Option<Rc<EventListener>> { if index < self.listeners.len() { Some(self.listeners.remove(index)) } else { None } } fn clear(&mut self) { self.listeners.clear(); } fn send(&self, event: &str) { for listener in &self.listeners { listener.on_event(event); } } } fn main() { let mut router = EventRouter::new(); { let listener = Rc::new(EventListener {}); router.add_listener(listener.clone()); router.send("Hello, world!"); router.remove_listener(0); } router.send("Hello, world!"); // 无输出,监听器已被移除 }
方案2:用 trait 对象封装回调(更灵活)
如果不需要保留EventListener结构体,可以直接将回调逻辑封装为 trait 对象,进一步简化代码:
use std::rc::Rc; struct EventRouter { callbacks: Vec<Rc<dyn Fn(&str)>>, } impl EventRouter { fn new() -> Self { Self { callbacks: Vec::new() } } fn add_callback(&mut self, callback: Rc<dyn Fn(&str)>) { self.callbacks.push(callback); } fn remove_callback(&mut self, index: usize) -> Option<Rc<dyn Fn(&str)>> { if index < self.callbacks.len() { Some(self.callbacks.remove(index)) } else { None } } fn clear(&mut self) { self.callbacks.clear(); } fn send(&self, event: &str) { for callback in &self.callbacks { callback(event); } } } fn main() { let mut router = EventRouter::new(); { let callback = Rc::new(|event| println!("{}", event)); router.add_callback(callback.clone()); router.send("Hello, world!"); router.remove_callback(0); } router.send("Hello, world!"); // 无输出 }
方案3:支持可变状态的监听器(单线程)
如果监听器需要修改内部状态,单线程下可以配合RefCell使用:
use std::rc::Rc; use std::cell::RefCell; struct EventListener { event_count: u32, } impl EventListener { fn on_event(&mut self, event: &str) { self.event_count += 1; println!("Received event '{}' (total: {})", event, self.event_count); } } struct EventRouter { listeners: Vec<Rc<RefCell<EventListener>>>, } impl EventRouter { fn new() -> Self { Self { listeners: Vec::new() } } fn add_listener(&mut self, listener: Rc<RefCell<EventListener>>) { self.listeners.push(listener); } fn send(&self, event: &str) { for listener in &self.listeners { listener.borrow_mut().on_event(event); } } } fn main() { let mut router = EventRouter::new(); let listener = Rc::new(RefCell::new(EventListener { event_count: 0 })); router.add_listener(listener.clone()); router.send("Test Event"); router.send("Test Event"); }
为什么原方案不可行?
原代码使用裸引用&EventListener存在两个核心问题:
- Rust要求
Vec中的所有引用必须具有相同的生命周期,无法存储不同生命周期的监听器; - 裸引用的生命周期必须至少和
EventRouter一致,否则会触发悬垂引用检查,即使你在运行时调用了clear,Rust的静态分析也不会认可这种动态操作。
内容的提问来源于stack exchange,提问作者abingham
相关产品推荐
相关产品推荐

