Associated Statics - a way to have high performance, (mostly) statically dispatched event busses


#1

I recently decided to look back on an event bus I wrote a long time ago. I had to do things like this back then:

/* some things omitted for simplicity */

pub trait Event {
  fn use_internal_data<F, T>(f: F) -> T where F: Fn(&InternalData<Self>) -> T;
  fn use_internal_data_mut<F, T>(f: F) -> T where F: Fn(&mut InternalData<Self>) -> T;
}

pub struct EventMetadata<T: Event> {
  handlers: Vec<EventHandlers<T>>,
}

impl<T: Event> EventMetadata<T> {
  pub fn new() -> EventMetadata<T> {
    EventMetadata { handlers: vec![] }
  }
  fn get_or_create_handlers(&mut self, bus: EventBus) -> &mut EventHandlers<T> {
    self.handlers.get(bus.id()) /* also some logic to grow the vec and stuff */
  }
}

(Yes, I now realize use_internal_data_mut can be exploited to set/replace the InternalData.)

The way this works is that you’d create a static EventMetadata (using lazy_static) for every impl you make, and pass it on as needed in places where it’s relevant. The goal is to provide (as close to) static dispatch (as possible) - with the EventBus code mostly just calling the EventMetadata functions, all usages of the Event basically boil down to getting the bus ID, and getting the vec entry for it. This is much faster than doing some unnecessary hash operations (which you would have to do if you were using, say, AnyMap).

However it would be nice to be able to do it like this instead:

pub trait Event {
  static INTERNAL: private::EventMetadata<Self> = /* something here, perhaps something with lazy initialization */;
}

mod private {
  pub struct EventMetadata<T: Event> {...}
}

And then the user doesn’t need to provide EventMetadata themselves (or be able to look at it), which can avoid quite a lot of confusion (as some might think EventMetadata lets you associate additional data to an event instance, while it’s metadata about the event type). This also simplifies the monomorphization case (as monomorphization can generate a static for every monomorph), so if you had a VecEvent<T>(Vec<T>) for example you wouldn’t need to impl a separate Event for every VecEvent<T> you wanna use. (or you might not be able to, if the VecEvent<T> is from another crate!)

The reason you can implement the Event trait even tho you can’t use eventbus::private::EventMetadata is because when using the default value the impl can be omitted. So you don’t ever reference the INTERNAL static in your trait impl, thus not having to reference its type either.


#2

For context, I’m trying to avoid things like this:

https://mozilla.logbot.info/rust/20180303#c14394317
https://mozilla.logbot.info/rust/20180303#c14395430
more importantly, this kind of confusion: https://mozilla.logbot.info/rust/20180303#c14395745 (cont https://mozilla.logbot.info/rust/20180303#c14395821 and https://mozilla.logbot.info/rust/20180303#c14395901 )


#3

It seems like we could support associated statics readily enough. (And, equivalently, generic statics and constants.)