Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trait bounds #1634

Closed
SoniEx2 opened this issue May 29, 2016 · 11 comments
Closed

Trait bounds #1634

SoniEx2 opened this issue May 29, 2016 · 11 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@SoniEx2
Copy link

SoniEx2 commented May 29, 2016

I was trying to modify my eventbus crate to be more strict about types. That is, so you could have EventBus<IrcEvent> and not be able to send GameEvents through it, unless the GameEvent is also an IrcEvent. So I tried this:

//#[derive(PartialEq, Eq, Hash)] // doesn't work for this
pub struct EventBus<E: ?Sized + Event> {
  uuid: Uuid,
  _e: PhantomData<E>
}

impl<E: ?Sized + Event> EventBus<E> {
  pub fn new() -> EventBus<E> {
    EventBus { uuid: Uuid::new_v4(), _e: PhantomData }
  }

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

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

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

And as it turns out I cannot currently use T: E, pass a trait to <E>, or anything like that, as Rust doesn't really like traits and traits are often considered lesser than types.

Note that being able to specify inheritance bounds like that would also benefit the typemap and anymap crates. Both have a shitty workaround for it that doesn't let you control the types you can use with them (and controlling those types would in turn benefit me on the eirc crate).

@Stebalien
Copy link
Contributor

Technically, you can sort of do this with T: Sized + Unsize<E> + Unsize<Event> but that's a hack. I'd also very much like to have a feature like this.

@withoutboats
Copy link
Contributor

withoutboats commented May 29, 2016

Traits are not lesser than types, they just aren't types (trait objects create a syntactic confusion here). Does anyone know of any Haskell extensions or anything where types are allowed to be parameterized by type classes? This seems like it would have weird implications.

My impression of your problem is that you have an EventBus which can take events. These events can be of different type, but must all be from the same source. This is how I would model that in Rust:

// A source
trait Source {
    ...
}

// An Event from a source 
trait Event {
    type Source: Source;
    ...
}

// An EventBus accepts events from only one source.
impl EventBus<S: Source> {
    // Register an Event which is from this source.
    fn register<T>(...) where T: Event<Source=S>;
    ...
}

An EventBus<Game> will take any event whose source is Game, whereas an EventBus<Irc> will take any event whose source is Irc.

If you want to define game events as having methods which aren't implemented for irc events, you could do something like this:

trait GameEvent: Event<Source=Game> {
    ...
}

trait IrcEvent: Event<Source=Irc> {
    ...
}

@SoniEx2
Copy link
Author

SoniEx2 commented May 29, 2016

@withoutboats And how do you handle IrcEvent, IrcClientEvent and IrcServerEvent, and limiting a bus to IrcClientEvent (which's trait IrcClientEvent : IrcEvent) and a different bus to IrcServerEvent (IrcServerEvent : IrcEvent) and yet another bus to all IrcEvent?

This would probably be most useful if you have an internal bus and an external bus, the internal bus for only your own events (for preprocessing and parsing and stuff) and the external bus shared with everything else. Instead of duplicating all your events you'd limit the internal bus to your events and then re-post on the external bus.

@SoniEx2
Copy link
Author

SoniEx2 commented May 29, 2016

@Stebalien That appears to be unstable. rust-lang/rust#27732

@withoutboats
Copy link
Contributor

withoutboats commented May 29, 2016

@withoutboats And how do you handle IrcEvent, IrcClientEvent and IrcServerEvent, and limiting a bus to IrcClientEvent (which's trait IrcClientEvent : IrcEvent) and a different bus to IrcServerEvent (IrcServerEvent : IrcEvent) and yet another bus to all IrcEvent?

So you want to be able to create a bus which handles all IrcClient and IrcServer events, and another that handles only the events of one of these two traits?

That sounds to me like an Event can be associated with multiple sources, making that a parameter rather than an associated type on Event.

trait Event<S: Source> {
   ...
}

impl EventBus<S: Source> {
    fn register<T>(...) where T: Event<S>;
}

Then I would implement Event<Irc> for all IrcEvents and Event<IrcClient> only for the events which are IrcClientEvents.

This is more flexible than a kind of type hierarchy - events can be associated with arbitrary sets of sources, rather than only "a source and all of its super-types".

@SoniEx2
Copy link
Author

SoniEx2 commented May 30, 2016

@withoutboats @Stebalien 's hack is (almost, see below) exactly what I'm looking for, if it were stable... (almost: the hack doesn't let you limit the bus to just a single event type)

And I'm not sure what the implications of your method would be...

@withoutboats
Copy link
Contributor

That hack also only works as long as the trait is object safe and isn't actually parameterization by traits.

@glaebhoerl
Copy link
Contributor

Does anyone know of any Haskell extensions or anything where types are allowed to be parameterized by type classes?

ConstraintKinds

@SoniEx2
Copy link
Author

SoniEx2 commented May 31, 2016

@withoutboats Your method would let events have different cancellability based on the source, which would be Very Bad™. I think I could split events into 2 traits: a base trait that sets cancellability and a filter trait which sets source, but that makes implementation a pain...

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 17, 2016
@nrc
Copy link
Member

nrc commented Aug 17, 2016

It seems like this requirement is met entirely by specialisation? Can we focus this issue on anything that is misssing once specialisation stabilises?

@Centril
Copy link
Contributor

Centril commented Oct 7, 2018

Closing in favor of #2190.

@Centril Centril closed this as completed Oct 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

6 participants