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 alias. #1733

Merged
merged 30 commits into from
Apr 24, 2017
Merged

Trait alias. #1733

merged 30 commits into from
Apr 24, 2017

Conversation

hadronized
Copy link
Contributor

@hadronized hadronized commented Aug 31, 2016

Rendered


Trait alias would enable aliasing traits.

Useful especially when we want to lift the name from generic to specific (a bit like Result on a lot of modules, but for traits):

mod gen {
  trait Foo<T> {
    // ...
  }
}

mod spec {
  struct Bck0 {}

  trait Foo as gen::Foo<Bck0>;
}

The idea is to add a new keyword or construct for enabling trait aliasing. One shouldn’t use the
`type` keyword as a trait is not a type and that could be very confusing.

The `trait TraitAlias as Trait` is suggested as a starter construct for the discussion.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not trait TraitAlias = Trait similar to the type keyword's syntax?
If I came across trait Foo as Bar I would probably read it as Bar being an alias for Foo.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the = syntax to mirror type aliases as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it’s a good idea as well, as would read the other way around, yeah.

@durka
Copy link
Contributor

durka commented Aug 31, 2016

Note that this is sugar for

trait Foo: gen::Foo<Bck0> {}
impl<T: gen::Foo<Bck0>> Foo for T {}

So since the functionality is already supported, it makes sense to add syntax. The = syntax would be intuitively analogous to type alias syntax (indeed, I've seen people ask why it doesn't work already). 👍

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

@durka, hm, are you sure it safely desugars to that? It creates a brand new trait, not really an alias.

@eddyb
Copy link
Member

eddyb commented Sep 1, 2016

@durka You can't implement Foo with gen::Foo's methods though.

@durka
Copy link
Contributor

durka commented Sep 1, 2016

You're right, the trait aliasing that I demonstrated only works for bounds. I didn't realize that this RFC would allow impl blocks as well. The RFC could benefit from an example!

@Diggsey
Copy link
Contributor

Diggsey commented Sep 6, 2016

@durka More importantly, the Foo and gen::Foo<Bck0> trait objects are incompatible, since rust does not support converting between trait objects.

I prefer the trait X = Y syntax for symmetry with type aliases, and it should support generics too, eg. trait X<T> = Y<T, Foo>

Also, will this be able to support multiple traits, ie. can I do trait X = Y + Z? While powerful and very useful it would be confusing that you couldn't use trait X as a trait object.

@hadronized
Copy link
Contributor Author

hadronized commented Sep 6, 2016

I think trait X = Y + Z should be supported as well, a bit like Haskell with the ConstraintKinds ghc extension that can be used to enable constraint aliasing (in which you find typeclasses).

@durka
Copy link
Contributor

durka commented Sep 6, 2016

The thing about trait X = Y + Z; is this:

In the simpler case of trait A = B;, then either A is a separate trait or it isn't. The "desugaring" I proposed (trait A: B {} impl<T: B> A for B {}) would make A and B separate traits so you wouldn't be able to cast between the trait objects. But it sounds like this RFC is going in the direction that they would actually be synonyms, so my desugaring doesn't quite work.

But Rust currently doesn't have any way to make a trait object of two non-builtin traits, like &(Y + Z). Therefore something like my desugaring would be the only way to make trait X = Y + Z; work.

Sure, we could have the same syntax do two different things, but that seems weird. It seems to me that for now we should just do trait A = B;, and then in the glorious future when Rust gains the ability to represent &(Y + Z), then trait X = Y + Z; automatically starts working.

@Diggsey
Copy link
Contributor

Diggsey commented Sep 6, 2016

@durka You're forgetting that traits are not just used as trait objects. trait X = Y + Z can work perfectly fine for static polymorphism, it just means that &X is not a valid trait object (just as &(Y + Z) is not a valid trait object.

@durka
Copy link
Contributor

durka commented Sep 6, 2016

@Diggsey I didn't forget, but I glossed over it :) For all purposes besides trait objects, my desugaring works and trait X = Y + Z; would be fine. But it seems weird that trait X = sometimes makes an objectifiable trait and sometimes not, depending on the RHS. But I guess that's the same kind of inconsistency as object safety anyway, so perhaps it's not so bad.

@kennytm
Copy link
Member

kennytm commented Sep 7, 2016

&(X + Y) can be supported if a subset of #1524 is accepted (making &(X + Y) a triple-word fat pointer) 😉

@withoutboats
Copy link
Contributor

What about HRTB? Would this be viable?

trait Bar<'a> { }

trait Foo = for<'a> Bar<'a>;

@eddyb
Copy link
Member

eddyb commented Sep 8, 2016

@withoutboats From an implementation standpoint, it shouldn't be more difficult than other trait aliases.
And I can certainly see its value, I've noticed people building helper traits for this in various situations.

@solson
Copy link
Member

solson commented Sep 14, 2016

A concrete example for hiding HRTB came up in #rust:

trait FromRef<T> = for<'a> From<&'a T>;

@kennytm
Copy link
Member

kennytm commented Sep 14, 2016

@solson

Would impl FromRef<u32> for SomeStruct { ... } work? (How would the signature of from be written though?)

Could the compiler auto-translate it to impl<'a> From<&'a u32> for SomeStruct { ... }?

@withoutboats
Copy link
Contributor

withoutboats commented Sep 14, 2016

I don't know how implementing FromRef would work, and it concerns me.

Here's From<&'a u32>.

impl<'a> From<&'a u32> for u32 {
    fn from(x: &'a u32) -> u32 { *x }
}

Note that the 'a shows up in the fn body. Here is FromRef<u32>:

impl FromRef<u32> for u32 {
    fn from(x: &u32) -> u32 { *x }
}

The normal meaning of this would be to elide a lifetime parameter on the from method, having an incompatible signature with the trait.

Special elisions for implementing HRTB trait aliases don't seem like a good idea to me (and AFAICT can't scale to an HRTB trait alias with multiple higher ranked lifetimes), but not being able to implement a trait alias transparently also seems like a problem.

@Diggsey
Copy link
Contributor

Diggsey commented Sep 14, 2016

@withoutboats I don't see a problem with not being able to implement a trait alias if you can't implement what it aliases to to start with.

It's not like you can impl for<'a> From<&'a u32> for u32 anyway.

@withoutboats
Copy link
Contributor

withoutboats commented Sep 14, 2016

impl for<'a> From<&'a u32> is morally equivalent to impl<'a> From<&'a u32> and it presents a pretty serious usability problem if this feature is used to obscure HRTB but it is unclear to users how they could provide an impl that satisfies this trait bound.

@Diggsey
Copy link
Contributor

Diggsey commented Sep 15, 2016

@withoutboats that's already the case for trait inheritance. For example, you can't implement Eq without implementing PartialEq, and you have to look at the definition of Eq to figure that out. With trait where clauses it can be even more complicated.

@withoutboats
Copy link
Contributor

withoutboats commented Sep 15, 2016

Sure, but in that case its relatively easy to learn what trait you need to implement. I'm concerned that using this feature to obscure HRTB will lead to a situation where a less expert Rust user sees that they need to implement FromRef, sees in the definition of FromRef trait FromRef<T> = for<'a> From<&'a T>; and then is baffled about how they could implement for<'a> From<&'a T>.

I don't have a solution; HRTB just present a pretty serious accessibility problem in general.

@kennytm
Copy link
Member

kennytm commented Sep 15, 2016

Current situation:

Ability Simple trait HRTB (for<'a> T<'a>) Composite (X + Y)
Constraint S: T
Impl trait -> impl T
Trait object &T
impl T for S

@durka
Copy link
Contributor

durka commented Sep 15, 2016

Is type FromRef = for<'a> From<&'a T>; needed if you can write type FromRef<'a> = From<&'a T>;?

@kennytm
Copy link
Member

kennytm commented Sep 15, 2016

@durka The point is to hide the for<'a> part, just like you can write F: Fn(&u32) -> &u32 without any explicit lifetime.

@aturon aturon self-assigned this Sep 29, 2016
@nikomatsakis
Copy link
Contributor

I think I'm generally in favor of adding some kind of shorthand for "trait aliases" (or "where-clause aliases", depending on your POV). @aturon and I talked about it recently and we felt like the syntax ought to be modeled on the syntax for declaring a new trait with supertraits, but with = replacing :. So if you had a declaration like this:

trait SymPartialEq<T> = PartialEq<T> where T: PartialEq<Self>;

then you could do:

fn foo<T, U>() where T: SymPartialEq<U>

and it would be equivalent to

fn foo<T, U>() where T: PartialEq<U>, U: PartialEq<T>

I'm not sure what to do about the case where you want just a where-clause though, and no =. I don't like trait Foo<T> where T: Bar<Self>, because it looks too much like a declaration of a new trait. I guess trait Foo<T> = where T: Bar<Self> might be ok.

Not sure if I love the precise syntax here, but there is something nice about the symmetry with supertrait clauses, and the "bias" to always have a Self type, just like regular traits.

It seems good for the more common cases that don't require where clauses, e.g.

trait Foo = Bar + Baz;

@cuviper
Copy link
Member

cuviper commented Nov 2, 2016

Maybe trait Foo<T> = _ where T: Bar<Self> or trait Foo<T> = * where T: Bar<Self> ?

@durka
Copy link
Contributor

durka commented Nov 2, 2016

What would be the difference between trait X = where Z; and trait X where Z {}?

On Wed, Nov 2, 2016 at 4:48 PM, Josh Stone notifications@github.com wrote:

Maybe trait Foo = _ where T: Bar or trait Foo = * where T:
Bar ?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#1733 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAC3nyx6H478DW2EceC6WEyOjopf7O_Uks5q6Pc5gaJpZM4JxjCo
.

@bluss
Copy link
Member

bluss commented Nov 2, 2016

How are associated types handled? I commonly use things like trait Scalar : Add<Output=Self> + Copy { } (with more operators).

glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 16, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 16, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 16, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 16, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 16, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 16, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 26, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jun 29, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
glyn added a commit to cloudfoundry/jvmkill that referenced this pull request Jul 5, 2017
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
@comex comex mentioned this pull request Oct 27, 2017
@clarfonthey
Copy link
Contributor

Haven't read all the comments, but by the existing grammar is trait Trait = ; allowed?

@durka
Copy link
Contributor

durka commented Feb 27, 2018 via email

@cramertj
Copy link
Member

cramertj commented Feb 27, 2018

I'd have guessed it would be a bound which accepted all types. If that works, I'd also expect trait Static = 'static; to be a bound which accepted all 'static types.

@durka
Copy link
Contributor

durka commented Feb 27, 2018 via email

@Boscop
Copy link

Boscop commented Mar 26, 2018

Would this also work?

trait Foo<'a> = Bar<'a> + 'a;

It should!

@hadronized
Copy link
Contributor Author

@Boscop The RFC enables it, yes.

bors added a commit to rust-lang/rust that referenced this pull request Nov 2, 2018
Implement trait aliases (RFC 1733)

Extends groundwork done in #45047, and fully implements rust-lang/rfcs#1733.

CC @durka @nikomatsakis
bors added a commit to rust-lang/rust that referenced this pull request Nov 3, 2018
Implement trait aliases (RFC 1733)

Extends groundwork done in #45047, and fully implements rust-lang/rfcs#1733.

CC @durka @nikomatsakis
@Centril Centril added A-syntax Syntax related proposals & ideas A-traits Trait system related proposals & ideas A-typesystem Type system related proposals & ideas labels Nov 23, 2018
@fmease fmease mentioned this pull request Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-syntax Syntax related proposals & ideas A-traits Trait system related proposals & ideas A-typesystem Type system related proposals & ideas final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.