Rfcs: Associated traits

Created on 27 Oct 2017  Â·  10Comments  Â·  Source: rust-lang/rfcs

RFC 1733 added trait aliases, like:

trait IntoIntIterator = IntoIterator<Item=i32>;

Well, why not allow putting those in traits, by analogy with associated types looking like type aliases? For example:

trait Handler {
    trait Arg;
    fn handle<ArgImpl: Self::Arg>(&self, arg: ArgImpl);
}

struct MyHandler;
impl Handler for MyHandler {
    trait Arg = IntoIterator<Item=i32>;
    fn handle<ArgImpl: Self::Arg>(&self, arg: ArgImpl) {
        for number in arg { println!("{}", number); }
    }
}

Example of a function that's generic over implementations of Handler:

fn example_generic_helper<HandlerImpl, ArgImpl>(handler: HandlerImpl, args: Vec<ArgImpl>)
    where HandlerImpl: Handler,
          ArgImpl: <HandlerImpl as Handler>::Arg {
    for arg in args {
        handler.handle(arg);
    }
}

Associated traits could also have supertrait bounds.

And if impl Trait syntax is extended to function arguments, they'd be a natural fit:

trait Handler {
    trait Arg;
    fn handle(&self, arg: impl Self::Arg);
}

(I was just writing some code where this could have come in handy.)

There's also the natural dual of allowing traits as generic parameters, just as associated types mirror regular type parameters and associated consts mirror the upcoming 'const generics'. Something like

fn foo<Impl, trait Trait> where Impl: Trait { … }

I think this has been proposed before.

T-lang

Most helpful comment

The equivalent feature in GHC is called ConstraintKinds. (Probably already mentioned in one of the linked threads, but worth mentioning again.)

All 10 comments

This could mix well with higher-kinded-types/#1598 (or whatever equivalent ends up getting implemented). Something like:

pub trait Map<K: Self::Key, V: Self::Value> {
    trait Key;
    trait Value;

    ...
}

impl<K: Hash, V> Map<K, V> for HashMap<K, V> {
    trait Key = Hash;
    trait Value = Any;

    ...
}

Ideally these would be more like 'associated type bounds'.

The equivalent feature in GHC is called ConstraintKinds. (Probably already mentioned in one of the linked threads, but worth mentioning again.)

Right now, traits can appear in type parameter position on traits, but presumably this only provides trait objects, not constraints. I suppose dyn https://github.com/rust-lang/rfcs/pull/2113 improves clarity but the trait remains necessary.

I'm interested in collaboration on an RFC regarding this.

Does it make sense to support this?

impl Handler for Foo {
    trait Arg = 'static;
}

@kennytm What is the use case you have in mind? It feels natural to support lifetime bounds.

@Centril something about this

trait PointerFamily {
    trait Bounds;
    type Pointer<T: ?Sized>;
}

struct ConstPtrFamily;
impl PointerFamily for ConstPtrFamily {
    trait Bounds = Copy + Ord + Hash + UnwindSafe + 'static;
    type Pointer<T: ?Sized> = *const T;
}

struct RefFamily<'a>(PhantomData<&'a ()>);
impl<'a> PointerFamily for RefFamily<'a> {
    trait Bounds = Copy + Send + Sync + UnwindSafe + 'a;
    type Pointer<T: ?Sized> = &'a T;
}

struct RcFamily;
impl PointerFamily for RcFamily {
    trait Bounds = Clone + UnwindSafe + 'static;
    type Pointer<T: ?Sized> = Rc<T>;
}

I've started a dedicated repo for research and writing on a ConstraintKinds RFC:

I'd love it if y'all would:

  • create issues with motivating examples
  • drop any ideas / complications as issues
  • collaborate -- if you want to help write the RFC, leave a note and I'll give you perms.

I've been working on something, and Associated traits would work really nicely with it.

trait Comp {
    type Props: Clone + 'static;
    trait Events;

    fn new(props: Self::Props) -> Self;
}

struct Label {
    value: String,
}

impl Comp for Label {
    type Props = LabelProps;
    trait Events = LabelEvents;

    fn new(props: Self::Props) -> Self {
        unimplemented!()
    }
}

struct LabelProps {
    value: String,
}

trait LabelEvents {
}

impl LabelEvents for OnClick {
    /* ... */
}

struct OnClick;
struct KeyPress;

struct Context<C: Comp> {
}

impl<C> Context<C> {

    fn listen<E>(&mut self, func: impl for<'r> Fn(&'r mut Context<C>, E)) where E: C::Events {
        unimplemented!()
    }
}

fn create_panel() {
    let ctx: &mut Context<Label> = unimplemented!();

    ctx.listen::<OnClick>(|ctx, event| {
        // Do something with the event
    });

    // Compile error.
    ctx.listen::<KeyPress>(|ctx, event| {
    });
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

rudolfschmidt picture rudolfschmidt  Â·  3Comments

burdges picture burdges  Â·  3Comments

p-avital picture p-avital  Â·  3Comments

camden-smallwood-zz picture camden-smallwood-zz  Â·  3Comments

onelson picture onelson  Â·  3Comments