首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >简单的类型安全和线程安全的锈蚀事件系统

简单的类型安全和线程安全的锈蚀事件系统
EN

Code Review用户
提问于 2016-04-13 20:14:02
回答 1查看 2.1K关注 0票数 2

我正在创建一个相对简单的类型安全和线程安全的锈蚀事件系统。它将与我正在制作的IRC库一起使用,并且在IRC库中使用,但是对于其他用例,它应该可以很好地工作。它需要合理的好和安全。使用它可能需要lazy-static,这让我有点担心,但即便如此,它也应该是安全的。

这是event.rs代码:

代码语言:javascript
复制
use std::collections::HashMap;
use std::marker::PhantomData;
extern crate uuid;
use self::uuid::Uuid;

// Note: This doesn't support Copy or Clone for safety reasons.
// More specifically, it should be impossible to unregister the same handler more than once.
pub struct EventHandlerId<T: Event + ?Sized> {
  id: Uuid,
  _t: PhantomData<T>,
}
impl<T: Event + ?Sized> Eq for EventHandlerId<T> {}
impl<T: Event + ?Sized> PartialEq for EventHandlerId<T> {
  fn eq(&self, other: &Self) -> bool {
    self.id == other.id && self._t == other._t
  }
}

struct EventHandler<T: Event + ?Sized> {
  priority: i32,
  f: fn(&mut T),
  id: EventHandlerId<T>,
}

pub struct EventMetadata<T: Event + ?Sized> {
  handlers: HashMap<&'static EventBus, Vec<EventHandler<T>>>,
}

impl<T: Event + ?Sized> EventMetadata<T> {
  pub fn new() -> EventMetadata<T> {
    EventMetadata { handlers: HashMap::new() }
  }

  fn put(&mut self, bus: &'static EventBus, f: fn(&mut T), priority: i32) -> EventHandlerId<T> {
    let vec = self.handlers.entry(bus).or_insert_with(Vec::new);
    let pos = vec.binary_search_by(|a| a.priority.cmp(&priority)).unwrap_or_else(|e| e);
    let id = Uuid::new_v4();
    vec.insert(pos, EventHandler { f: f, priority: priority, id: EventHandlerId { id: id, _t: PhantomData } });
    EventHandlerId { id: id, _t: PhantomData }
  }

  fn remove(&mut self, bus: &EventBus, f: EventHandlerId<T>) {
    let flag = self.handlers.get_mut(bus).iter_mut().any(|v| { v.retain(|x| x.id != f); v.is_empty() });
    if flag { self.handlers.remove(bus); }
  }

  #[inline]
  fn post(&self, bus: &EventBus, event: &mut T) -> bool {
    self.handlers.get(bus).iter().flat_map(|x| x.iter()).any(|h| {
      (h.f)(event);
      event.cancelled()
    })
  }
}

pub trait Event {
  // type properties
  fn event_metadata<F, R>(F) -> R where F: FnOnce(&EventMetadata<Self>) -> R;

  fn mut_metadata<F, R>(F) -> R where F: FnOnce(&mut EventMetadata<Self>) -> R;

  fn cancellable() -> bool { false }

  // instance properties
  fn cancelled(&self) -> bool { false }

  fn cancel(&self, bool) { panic!() }
}

#[derive(PartialEq, Eq, Hash)]
pub struct EventBus {
  uuid: Uuid
}

impl EventBus {
 pub fn new() -> EventBus {
   EventBus { uuid: Uuid::new_v4() }
 }

 pub fn register<T>(&'static self, f: fn(&mut T), priority: i32) -> EventHandlerId<T> where T: Event {
   T::mut_metadata(|x| x.put(self, f, priority))
 }

 pub fn unregister<T>(&self, f: EventHandlerId<T>) where T: Event {
   T::mut_metadata(|x| x.remove(self, f))
 }

 pub fn post<T>(&self, event: &mut T) -> bool where T: Event {
   T::event_metadata(|x| x.post(self, event))
 }
}

它的用途如下:(main.rs)

代码语言:javascript
复制
mod event;
use event::{EventBus, EventMetadata, Event};
use std::sync::RwLock;

#[macro_use]
extern crate lazy_static;

struct NoEvent {
  i: i32
}

lazy_static! {
  static ref NOEVENT_METADATA: RwLock<EventMetadata<NoEvent>> = RwLock::new(EventMetadata::new());
  static ref EVENT_BUS: EventBus = EventBus::new();
}

impl Event for NoEvent {
  fn event_metadata<F, R>(f: F) -> R where F: FnOnce(&EventMetadata<Self>) -> R {
    f(&*NOEVENT_METADATA.read().unwrap())
  }

  fn mut_metadata<F, R>(f: F) -> R where F: FnOnce(&mut EventMetadata<Self>) -> R {
    f(&mut *NOEVENT_METADATA.write().unwrap())
  }
}

fn test(e: &mut NoEvent) {
  println!("{}", e.i);
  e.i += 1;
}

fn test2(e: &mut NoEvent) {
  println!("{}", e.i);
}

fn main() {
  let test_id = EVENT_BUS.register(test, 0);
  let mut event = NoEvent { i: 3 };
  EVENT_BUS.post(&mut event);
  EVENT_BUS.register(test2, 1);
  EVENT_BUS.post(&mut event);
  EVENT_BUS.unregister(test_id);
  EVENT_BUS.post(&mut event);
}
EN

回答 1

Code Review用户

发布于 2016-04-13 23:07:55

  1. 锈蚀标准压痕为4个空格。代码当前有1和2个空格缩进。
  2. where子句应该放在下一行,每一行限制一行。
  3. extern crate通常先于use语句。
  4. 不需要use self::uuid::...,您可以只使用use uuid::...
  5. 当存在类型参数时,#[inline]是隐式的--内联代码的相同机制是它是如何被单级化的。
  6. 为什么在等式检查中包括PhantomData?该实现总是返回true
  7. UUID的好处是什么?例如,使用单调递增的原子变量会有负面影响吗?
  8. 当我想到一个事件总线时,我假设我将给总线提供一个完整的值,而不是引用一个。为什么代码会做出这个决定?
  9. 总的来说,有很多复杂的事情并没有立即从这些例子中消失。你能解释一下当前的设计是如何发展的吗?
  10. 您可能需要研究如何使用BTreeMap来保存处理程序。键可以是优先级,值可以是处理程序的向量。docs并不保证这一点,但从实验上看迭代器是按顺序排列的,插入也应该是有效的。
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/125614

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档