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

Tracking issue for trait aliases #41517

Open
withoutboats opened this Issue Apr 24, 2017 · 48 comments

Comments

Projects
None yet
@withoutboats
Contributor

withoutboats commented Apr 24, 2017

This is a tracking issue for trait aliases (rust-lang/rfcs#1733).

TODO:

Unresolved questions:

  • Are bounds on other input types than the receiver enforced or implied?
@durka

This comment has been minimized.

Contributor

durka commented May 7, 2017

I think #24010 (allowing aliases to set associated types) should be mentioned here.

bors added a commit that referenced this issue Jun 5, 2017

Auto merge of #42125 - petrochenkov:privty, r=<try>
Check types for privacy

This PR implements late post factum checking of type privacy, as opposed to early preventive "private-in-public" checking.
This will allow to turn private-in-public checks into a lint and make them more heuristic-based, and more aligned with what people may expect (e.g. reachability-based behavior).

Types are privacy-checked if they are written explicitly, and also if they are inferred as expression or pattern types.
This PR checks "semantic" types and does it unhygienically, this significantly restricts what macros 2.0 (as implemented in #40847) can do (sorry @jseyfried) - they still can use private *names*, but can't use private *types*.
This is the most conservative solution, but hopefully it's temporary and can be relaxed in the future, probably using macro contexts of expression/pattern spans.

Traits are also checked in preparation for [trait aliases](#41517), which will be able to leak private traits, and macros 2.0 which will be able to leak pretty much anything.

This is a [breaking-change], but the code that is not contrived and can be broken by this patch should be guarded by `private_in_public` lint. [Previous crater run](#34537 (comment)) discovered a few abandoned crates that weren't updated since `private_in_public` has been introduced in 2015.

cc #34537 https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504
Fixes #30476
Fixes #33479

cc @nikomatsakis
r? @eddyb

bors added a commit that referenced this issue Jun 19, 2017

Auto merge of #42125 - petrochenkov:privty, r=alexcrichton
Check types for privacy

This PR implements late post factum checking of type privacy, as opposed to early preventive "private-in-public" checking.
This will allow to turn private-in-public checks into a lint and make them more heuristic-based, and more aligned with what people may expect (e.g. reachability-based behavior).

Types are privacy-checked if they are written explicitly, and also if they are inferred as expression or pattern types.
This PR checks "semantic" types and does it unhygienically, this significantly restricts what macros 2.0 (as implemented in #40847) can do (sorry @jseyfried) - they still can use private *names*, but can't use private *types*.
This is the most conservative solution, but hopefully it's temporary and can be relaxed in the future, probably using macro contexts of expression/pattern spans.

Traits are also checked in preparation for [trait aliases](#41517), which will be able to leak private traits, and macros 2.0 which will be able to leak pretty much anything.

This is a [breaking-change], but the code that is not contrived and can be broken by this patch should be guarded by `private_in_public` lint. [Previous crater run](#34537 (comment)) discovered a few abandoned crates that weren't updated since `private_in_public` has been introduced in 2015.

cc #34537 https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504
Fixes #30476
Fixes #33479

cc @nikomatsakis
r? @eddyb

bors added a commit that referenced this issue Jul 7, 2017

Auto merge of #42125 - petrochenkov:privty, r=nikomatsakis
Check types for privacy

This PR implements late post factum checking of type privacy, as opposed to early preventive "private-in-public" checking.
This will allow to turn private-in-public checks into a lint and make them more heuristic-based, and more aligned with what people may expect (e.g. reachability-based behavior).

Types are privacy-checked if they are written explicitly, and also if they are inferred as expression or pattern types.
This PR checks "semantic" types and does it unhygienically, this significantly restricts what macros 2.0 (as implemented in #40847) can do (sorry @jseyfried) - they still can use private *names*, but can't use private *types*.
This is the most conservative solution, but hopefully it's temporary and can be relaxed in the future, probably using macro contexts of expression/pattern spans.

Traits are also checked in preparation for [trait aliases](#41517), which will be able to leak private traits, and macros 2.0 which will be able to leak pretty much anything.

This is a [breaking-change], but the code that is not contrived and can be broken by this patch should be guarded by `private_in_public` lint. [Previous crater run](#34537 (comment)) discovered a few abandoned crates that weren't updated since `private_in_public` has been introduced in 2015.

cc #34537 https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504
Fixes #30476
Fixes #33479

cc @nikomatsakis
r? @eddyb
@durka

This comment has been minimized.

Contributor

durka commented Oct 2, 2017

I'd like to take a crack at this (starting with parsing).

@durka durka referenced this issue Oct 5, 2017

Merged

trait alias infrastructure #45047

7 of 10 tasks complete

@glaebhoerl glaebhoerl referenced this issue Oct 16, 2017

Open

Allocator traits and std::heap #32838

5 of 12 tasks complete
@carllerche

This comment has been minimized.

Member

carllerche commented Nov 3, 2017

I read the RFC and I saw a call out to Service, but I am not sure if the RFC actually solves the Service problem.

Specifically, the "alias" needs to provide some additional associated types:

See this snippet: https://gist.github.com/carllerche/76605b9f7c724a61a11224a36d29e023

Basically, you rarely want to just alias HttpService to Service<Request = http::Request> You really want to do something like this (while making up syntax):

trait HttpService = Service<http::Request<Self::RequestBody>> {
    type RequestBody;
}

In other words, the trait alias introduces a new associated type.

The reason why you can't do: trait HttpService<B> = Service<http::Request<B>> is that then you end up getting into the "the type parameter B is not constrained by the impl trait, self type, or predicates" problem.

@phaazon

This comment has been minimized.

phaazon commented Nov 3, 2017

Basically, you rarely want to just alias HttpService to Service<Request = http::Request>

Rarely? How do you define that?

The syntax you suggest seems a bit complex to me and non-intuitive. I don’t get why we couldn’t make an exception in the way the “problem” shows up. Cannot we just hack around that rule you expressed? It’s not a “real trait”, it should be possible… right?

@carllerche

This comment has been minimized.

Member

carllerche commented Nov 3, 2017

@phaazon rarely with regards to the service trait. This was not a general statement for when you would want trait aliasing.

Also, the syntax was not meant to be a real proposal. It was only to illustrate what I was talking about.

@phaazon

This comment has been minimized.

phaazon commented Nov 3, 2017

I see. Cannot we just use free variables for that? Like, Service<Request = http::Request> implies the free variable used in http::request<_>?

@carllerche

This comment has been minimized.

Member

carllerche commented Nov 3, 2017

@phaazon I don't understand this proposal.

bors added a commit that referenced this issue Dec 13, 2017

Auto merge of #45047 - durka:trait-alias, r=petrochenkov
trait alias infrastructure

This will be an implementation of trait aliases (RFC 1733, #41517).

Progress so far:

- [x] Feature gate
- [x] Add to parser
  - [x] `where` clauses
    - [x] prohibit LHS type parameter bounds via AST validation #45047 (comment)
- [x] Add to AST and HIR
  - [x] make a separate PathSource for trait alias contexts #45047 (comment)
- [x] Stub out enough of typeck and resolve to just barely not ICE

Postponed:

- [ ] Actually implement the alias part
- [ ] #21903
- [ ] #24010

I need some pointers on where to start with that last one. The test currently does this:

```
error[E0283]: type annotations required: cannot resolve `_: CD`
  --> src/test/run-pass/trait-alias.rs:34:16
   |
34 |     let both = foo();
   |                ^^^
   |
   = note: required by `foo`
```

bors added a commit that referenced this issue Dec 13, 2017

Auto merge of #45047 - durka:trait-alias, r=petrochenkov
trait alias infrastructure

This will be an implementation of trait aliases (RFC 1733, #41517).

Progress so far:

- [x] Feature gate
- [x] Add to parser
  - [x] `where` clauses
    - [x] prohibit LHS type parameter bounds via AST validation #45047 (comment)
- [x] Add to AST and HIR
  - [x] make a separate PathSource for trait alias contexts #45047 (comment)
- [x] Stub out enough of typeck and resolve to just barely not ICE

Postponed:

- [ ] Actually implement the alias part
- [ ] #21903
- [ ] #24010

I need some pointers on where to start with that last one. The test currently does this:

```
error[E0283]: type annotations required: cannot resolve `_: CD`
  --> src/test/run-pass/trait-alias.rs:34:16
   |
34 |     let both = foo();
   |                ^^^
   |
   = note: required by `foo`
```

bors added a commit that referenced this issue Dec 13, 2017

Auto merge of #45047 - durka:trait-alias, r=petrochenkov
trait alias infrastructure

This will be an implementation of trait aliases (RFC 1733, #41517).

Progress so far:

- [x] Feature gate
- [x] Add to parser
  - [x] `where` clauses
    - [x] prohibit LHS type parameter bounds via AST validation #45047 (comment)
- [x] Add to AST and HIR
  - [x] make a separate PathSource for trait alias contexts #45047 (comment)
- [x] Stub out enough of typeck and resolve to just barely not ICE

Postponed:

- [ ] Actually implement the alias part
- [ ] #21903
- [ ] #24010

I need some pointers on where to start with that last one. The test currently does this:

```
error[E0283]: type annotations required: cannot resolve `_: CD`
  --> src/test/run-pass/trait-alias.rs:34:16
   |
34 |     let both = foo();
   |                ^^^
   |
   = note: required by `foo`
```

bors added a commit that referenced this issue Dec 14, 2017

Auto merge of #45047 - durka:trait-alias, r=petrochenkov
trait alias infrastructure

This will be an implementation of trait aliases (RFC 1733, #41517).

Progress so far:

- [x] Feature gate
- [x] Add to parser
  - [x] `where` clauses
    - [x] prohibit LHS type parameter bounds via AST validation #45047 (comment)
- [x] Add to AST and HIR
  - [x] make a separate PathSource for trait alias contexts #45047 (comment)
- [x] Stub out enough of typeck and resolve to just barely not ICE

Postponed:

- [ ] Actually implement the alias part
- [ ] #21903
- [ ] #24010

I need some pointers on where to start with that last one. The test currently does this:

```
error[E0283]: type annotations required: cannot resolve `_: CD`
  --> src/test/run-pass/trait-alias.rs:34:16
   |
34 |     let both = foo();
   |                ^^^
   |
   = note: required by `foo`
```

bors added a commit that referenced this issue Dec 14, 2017

Auto merge of #45047 - durka:trait-alias, r=petrochenkov
trait alias infrastructure

This will be an implementation of trait aliases (RFC 1733, #41517).

Progress so far:

- [x] Feature gate
- [x] Add to parser
  - [x] `where` clauses
    - [x] prohibit LHS type parameter bounds via AST validation #45047 (comment)
- [x] Add to AST and HIR
  - [x] make a separate PathSource for trait alias contexts #45047 (comment)
- [x] Stub out enough of typeck and resolve to just barely not ICE

Postponed:

- [ ] Actually implement the alias part
- [ ] #21903
- [ ] #24010

I need some pointers on where to start with that last one. The test currently does this:

```
error[E0283]: type annotations required: cannot resolve `_: CD`
  --> src/test/run-pass/trait-alias.rs:34:16
   |
34 |     let both = foo();
   |                ^^^
   |
   = note: required by `foo`
```
@varkor

This comment has been minimized.

Member

varkor commented Feb 21, 2018

@durka: how's the work on the follow-up to #45047 going?

@clarcharr

This comment has been minimized.

Contributor

clarcharr commented Feb 27, 2018

Something I mentioned in the RFC: trait Trait =; is accepted by the proposed grammar and I think that this is a bit weird. Perhaps maybe the proposed _ syntax might be more apt here, because I think that allowing empty trait requirements is useful.

@durka

This comment has been minimized.

Contributor

durka commented Feb 27, 2018

@clarcharr

This comment has been minimized.

Contributor

clarcharr commented Feb 27, 2018

One other thing to note as a general weirdness is macro expansions involving trait bounds. Currently, fn thing<T:>() is valid syntax but perhaps fn thing<T: _>() should be the recommended version.

But then, is _ + Copy or something okay? I'm not sure. I would just suggest Any but that has different guarantees.

@petrochenkov

This comment has been minimized.

Contributor

petrochenkov commented Feb 27, 2018

Empty bound lists (and other lists) are accepted in other contexts as well, e.g. fn f<T: /*nothing*/>() { ... }, so trait Trait = /*nothing*/; being accepted is more of a rule than an exception.

@clarcharr

This comment has been minimized.

Contributor

clarcharr commented Feb 27, 2018

I think it makes sense being accepted, although I wonder if making it the canonical way to do so outside of macros is the right way to go. We already have been pushing toward '_ for elided lifetimes in generics, for example.

@varkor

This comment has been minimized.

Member

varkor commented Aug 5, 2018

but that doesn't mean they won't appear to function as traits or even necessarily need to use a different keyword.

I think trait is very misleading, precisely because this point is false. If "trait aliases" worked like type aliases, then you would be able to do something like the following:

trait Trait {}

trait Alias = Trait;

impl Alias for () {}

As it stands, you cannot, because so-called "trait" aliases are actually bounds aliases. Traits and bounds are different (even specifically trait bounds), both from an abstract model point-of-view, and their functionality within the language.

Using the term "trait alias" for this terminology is confusing and prevents true trait aliases from being added in a consistent manner with type aliases.

As example of a related place where people use slightly incorrect terminology to no ill effect

The use of terminology by people is very different to its realisation in the language. People might say "trait" instead of "trait bound" in practice, but conflating the two inside the language is, I would argue, a mistake.

@clarcharr

This comment has been minimized.

Contributor

clarcharr commented Aug 5, 2018

I believe that in practice there shouldn't be a visible difference between trait Alias: Trait where Self: Other {} impl<T: Trait> Alias for T where Self: Other {} and trait Alias = Trait.

So, in this case, impl Alias doesn't even make sense, because you've already defined a blanket impl.

@varkor

This comment has been minimized.

Member

varkor commented Aug 5, 2018

@clarcharr: I'm thinking more in terms of renaming, like in the following:

struct S;
trait T1 {}
trait T2 = T1;
impl T2 for S {}

This is the intuitive meaning (as far as I can see it) of the term "trait alias". It's analogous to how type aliases work. Note that you would also be able to use these trait aliases in bounds (aliases would be functionally identical to traits).

Whereas "bounds aliases" would alias both traits and lifetimes, specifically in the context of bounds on generic parameters, etc.).

@clarcharr

This comment has been minimized.

Contributor

clarcharr commented Aug 5, 2018

I assume you mean impl T1 for dyn T2, right? Or impl<T: T2> T1 for T {} ?

The latter will work as expected. Although the former basically, what you're asking is the meaning of what impl T2 and dyn T2 mean. Which… again, I think work in the context of trait aliases as both trait aliases and bound aliases.

@varkor

This comment has been minimized.

Member

varkor commented Aug 5, 2018

@clarcharr: sorry, I got that line completely muddled. I've updated it. I mean that we implement a trait by using its alias. (Obviously in this example, there's no point, but you could imagine something with generic parameters, etc. being useful to use in this way.)

@glandium

This comment has been minimized.

Contributor

glandium commented Aug 5, 2018

anecdata, but I would find bounds to be more confusing than trait.

@durka

This comment has been minimized.

Contributor

durka commented Aug 6, 2018

We actually did go back and forth on whether to use the keyword bound or maybe constraint in the RFC, but didn't come to a firm conclusion. Personally I don't see the huge need for a new keyword. You can use a trait as a bound... you can use a trait alias as a bound.

Would it make it better if you could impl an alias? We talked about that too but there are some gotchas. I very much encourage those now debating "trait"/"bound" to read these comments as some issues were already brought up.

The important thing is to get it implemented so we can experiment with these things.

@varkor

This comment has been minimized.

Member

varkor commented Aug 6, 2018

You can use a trait as a bound... you can use a trait alias as a bound.

Yes, it works in one direction but not the other. You can use a trait as a bound. You cannot use a bound as a trait. If we did want to introduce actual trait aliases in the future, we've blocked the obvious keyword.

We talked about that too but there are some gotchas.

Those aren't gotchas. Those are precisely the differences between traits and generic bounds. The issue is exactly that those two things are being conflated, just because a generic bound can consist of a single trait.

We can't just pretend that traits and bounds are the same thing. They have practical difference, which as @nrc pointed out in the original thread, are certainly going to cause people unfamiliar with the feature confusion.

Both features seem useful. If, in theory, we wanted both features in the language, we couldn't use the same keyword, because it's ambiguous in the case of a single trait. Therefore we need to make this distinction. Using trait, so that you can do something like trait Alias = Foo + Bar; is absolutely going to confuse beginners about what a trait actually is, because it seems to be saying that Foo + Bar is a trait.

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Aug 6, 2018

Thanks for finding the discussion @durka, I certainly remember having this conversation so I know it wasn't ignored.

In general, on a lot of the tracking issues for unimplemented extensions, I've seen people revisit questions from the RFC, usually syntactic ones. Syntactic questions are some of the hardest & most subjective to make a call on, and in my experience, when there's no actual implementation, the "hypothetical" questions get exhausted after a short time, and the conversation kind of goes in circles after that. I think one of the points of the RFC FCP period is to put a stop on hypothetical conversations until we actually have an implementation which can inform our decision again.

That is, I would personally find it more productive if we waited on re-opening syntactic or "bikesheddy" conversations on the tracking issue until we have an implementation to give more feedback into the conversation.

@phaazon

This comment has been minimized.

phaazon commented Aug 6, 2018

Using trait, so that you can do something like trait Alias = Foo + Bar; is absolutely going to confuse beginners about what a trait actually is, because it seems to be saying that Foo + Bar is a trait.

Yes, Foo + Bar is not a trait and I truly think that the terminology used in Rust is wrong. A bound – the terminology – shouldn’t even exist. The correct terminology to talk about here is a constraint.

trait Foo { /* … */ }

Foo is a trait which implementation yields a constraint. People can use that constraint with the Foo identifier.

trait Bar = Quux + Zoo + 'static;

Bar is a trait which implementation yields a constraint. People can use that constraint with the Bar identifier.

Using the current terminology, trait Trait { … } defines the constraint / contract a type must implement whenever it appears in a bound and trait Trait = Something; doesn’t define anything but expose a new compound contract a type must implement whenever it appears in a bound. A trait is never actually used as-is in the current Rust language definition as you always need to use a trait bound. And a bound doesn’t make sense if it’s not bound to a free type variable. When you define you trait alias, you have no such free variable, so there’s no bound here.

So, nah, I don’t think people will get confused if the feature is explained in sufficient clarity. Maybe the terminology of Rust should be enhance to clearly make the difference between:

  • A trait.
  • A trait bound.
  • Constraints.
@varkor

This comment has been minimized.

Member

varkor commented Aug 6, 2018

@withoutboats: I have no objection to features being implemented to get a feel for them. What my concern is that after a feature is implemented, it's stabilised while glossing over the potential problems that were not properly addressed during the RFC process. I don't care if my point is addressed now, but I would appreciate it being considered before stabilisation. The tracking issue is the location for design comments now, and as you say, since the design is not stable yet, that means it's open for discussion. I'm leaving my comments here so they can be taken into account at the right time, or discussed.

@phaazon:

A trait is never actually used as-is in the current Rust language definition as you always need to use a trait bound.

It may appear this way on the surface, which is why I think this is a subtle issue. A trait does exist as a standalone concept in the Rust language (just like lifetimes do), specifically in the context of traits applied to types. <Type as Trait> for instance, is about an "instance" (forgive the OO terminology) of a trait. Any time you directly call a trait method on a value, you're making use of the fact that traits are a concrete concept in the language. But in order to express that something even can be treated in this way requires a corresponding trait bound. Trait bounds are exactly what allow you to use traits.

A bound – the terminology – shouldn’t even exist. The correct terminology to talk about here is a constraint.

Personally, I don't really mind what terminology is used here (although "bound" is consistent with existing usage). I just want to emphasise that there is (both a theoretical and a functional) difference between: traits; lifetimes; generic bounds / trait bounds / constraints.

Maybe the terminology of Rust should be enhance to clearly make the difference

I'm always up for clarifications of terminology, especially in situations like this where the distinction is subtle. My point really boils down to wanting to clarify the terminology in-language as well as in documentation.

@varkor

This comment has been minimized.

Member

varkor commented Aug 6, 2018

Sorry, I know my comments might be coming off a little bit antagonistic, and that's not what I'm intending!

(Again, this post need not be addressed now, or for a long time. I want to post it now because I actually had time to read through the entire RFC thread, so it's fresh in my mind. My thoughts are not going to change after an implementation — the feature's intention is already clear. I would have raised the same point if I'd been around during the RFC period.)

Essentially, I feel like the decisions that were made in the original RFC mirror those that were made in favour of argument-position impl Trait, which (regardless of my or your particular opinion) has proven to be an unpopular choice (compared to the majority of Rust features which are accepted with strong support).

I want to make a few comments in response to rust-lang/rfcs#1733 (comment) (I know it was made a long time ago, but I think it's still relevant).

I don't think its a good idea to "up front" the understanding of the distinction between traits and bounds. Users can get pretty far not knowing the difference, its once they are more expert these sort of distinctions can be uncovered.

This is very like the argument that "users can get pretty far with an intuitive understanding of impl Trait, in which case argument-position and return-position just make sense". The problem is that:
(a) either users will muddle along, not noticing the oddity ("oh, I must just not understand Rust enough yet")
(b) they will be confused by what the difference is, trying to work out the distinction (not realising that the syntax is actively misleading them, making these concepts even harder to learn to become a better Rust programmer)
(c) eventually, when they learn the difference, they will realise there's an inconsistency (they may or may not care at this point, as these discussions have shown)

This comment asserts that (a) is most likely to happen and when (c), most users won't care. But it's not fair to make that assertion, as someone who's been involved in the design process and understands Rust much, much better than a beginner.

In addition there are the negative externalities of introducing more keywords and especially more contextual keywords.

Agreed. The disadvantage with acknowledging them as bounds aliases is that you need a new keyword. But there's a disadvantage in using an existing keyword in that you either:
(a) can't implement actual trait bounds in the future, because the keyword is already used
(b) have to overload the meaning (which is inconsistent, ugly, and leads to confusing concepts for users — see above point)

I'm sure many of those involved with the discussions have programmed in languages filled with inconsistencies and warts. From a "language beauty" perspective, this is really ugly. It's sacrificing temporary convenience for practicality, with a very minor gain. I'm not sure how, understanding what traits, lifetimes and bounds actually mean in Rust, one could look at the following and not feel any smidgen of regret:

trait Foo = 'static;

This approach does not help. We need to make things simple for the users. And, as I've been repeatedly reminded myself, assuming we know what's easier for beginners is not something we (as more experienced users of the language) can make. My feeling is that using the trait keyword for bounds is going to be more confusing to users than a new keyword.

@alexreg

This comment has been minimized.

Contributor

alexreg commented Aug 6, 2018

I think trait is very misleading, precisely because this point is false. If "trait aliases" worked like type aliases, then you would be able to do something like the following:

I was agreeing with @varkor precisely for this point. Type aliases are not really a misnomer as "trait aliases" would be, since you can still use them in virtually all the places type names can be used, as far as I remember.

i.e.

impl Trait for TypeAlias { ... }

works, but

impl TraitAlias for Type { ... }

does not work according to the above RFC. Nor is their an intuitive way to make it work.

Calling them bounds aliases is just much more accurate.

(The trait Foo = 'static; example is also a good argument against calling them "trait aliases", though I think the above is the strongest point.)

@jplatte

This comment has been minimized.

Contributor

jplatte commented Aug 6, 2018

@alexreg Your second code block should probably be

impl TraitAlias for Type { ... }

I've also tried the first thing and it does work. I wonder why anybody would want to do that but oh well.

@varkor Your point about <Type as Trait> actually seems like a much stronger argument for renaming this feature to me: If there were actual trait aliases (as in aliases for a single trait that could be used as in the example above), it would make a lot of sense to also support <Type as TraitAlias>. I'm not so sure if it would make sense with this feature – it might be handy in a very small amount of cases, but wouldn't at all make sense for things like trait Foo = 'static;.

Re. "bound aliases": Is this not the difference between a bound and a constraint?

impl<T> Send for Arc<T> where
    T: Send + Sync + ?Sized, 
       ~~~~~~~~~~~~~~~~~~~~ constraint
    ~~~~~~~~~~~~~~~~~~~~~~~ bound

And in that case, wouldn't it a lot more sense to talk about "constraint aliases"?

@alexreg

This comment has been minimized.

Contributor

alexreg commented Aug 6, 2018

@alexreg Your second code block should probably be

Yep, copy & paste coding fail, thanks for pointing it out! I fixed it now.

@alexreg

This comment has been minimized.

Contributor

alexreg commented Aug 6, 2018

As for <Type as TraitAlias>, I considered this too, but since this makes no sense where TraitAlias can actually be a bounds alias including multiple traits, it reinforces the case.

@phaazon

This comment has been minimized.

phaazon commented Aug 7, 2018

It seems like everyone seems to agree about the fact that the term bound alias is more accurate than trait alias. Would this syntax suit us?

pub bound StaticDebugClone = 'static + Debug + Clone; // (1)
pub constraint StaticDebugClone = 'static + Debug + Clone; (2)

It would then preclude the problem about implementing a trait alias – since implementing a bound doesn’t make sense.

Also, we should cope with HRTB. Is this possible then?

trait RefTrait<'a> {
  type Target: 'a;

  fn ref(&'a self) -> Self::Target;
}

bound Free = for<'a> RefTrait<'a>;

fn test_it<T>(t: T) where T: Free {
  let target = t.ref();
  // …
}
@phaazon

This comment has been minimized.

phaazon commented Aug 7, 2018

I’m especially interested in how we should cope with factored existential quantification, i.e.:

where for<'a> T: Foo<'a> + Bar<'a>

If we use the constraint terminology instead, we can for instance do this:

constraint FooBar<'a> = Foo<'a> + Bar<'a>;

where T: for<'a> FooBar<'a>
@RalfJung

This comment has been minimized.

Member

RalfJung commented Aug 7, 2018

Re. "bound aliases": Is this not the difference between a bound and a constraint?

impl<T> Send for Arc<T> where
    T: Send + Sync + ?Sized, 
       ~~~~~~~~~~~~~~~~~~~~ constraint
    ~~~~~~~~~~~~~~~~~~~~~~~ bound

Correct, good point! Internally in the compiler, things like Trait+Send or for<'a> Trait<'a> -- things you can put after a T: in a where clause -- are called "bounds". T: $bound is one example of a "where predicate", and in its most general form it has another level of quantification: for<...> T: $bound. There are two other kinds of where predicates: region predicates ('a : 'b), and equality predicates (which are unstable). "constraint" is not used in actual type names / enum variants, from what I can tell, but it is used in comments and seems to be the same as a "where predicate".

So "bound alias" would probably be the correct terminology, but TBH I find that it sounds rather awful.

factored existential quantification

This is universal quantification. ;)

But yes, for<'a> T: Foo<'a> + Bar<'a> is a "where predicate". T: for<'a> Foo<'a> + Bar<'a> is an equivalent predicate. You transformed your predicate/constrained from the first to the second form when introducing the alias, but that should not change its meaning.

If we use the constraint terminology instead, we can for instance do this:

These are not constraints/where predicates though -- Foo<'a> + Bar<'a> is a bound.

@Centril

This comment has been minimized.

Contributor

Centril commented Sep 1, 2018

@RalfJung

"constraint" is not used in actual type names / enum variants, from what I can tell, but it is used in comments and seems to be the same as a "where predicate".

The terminology comes from Haskell where Eq :: * -> Constraint and Int :: * and Eq Int :: Constraint. Here, * is the kind of types and Constraint is the kind of "satisfying a type-class".
Traits are therefore type-level functions from types to a "fact of implementation".

I think folks should take a look at Data.Constraint. It might be a useful experience to understand that stuff.

@Nemo157

This comment has been minimized.

Contributor

Nemo157 commented Nov 4, 2018

Now that we have an implementation in nightly, it appears my earlier question about whether a trait alias brings methods into scope is no. Using @pengowen123's test case on the playground gives

error[E0599]: no method named `foo` found for type `some_module::Qux` in the current scope

Is this the intended design, or just a current implementation limitation?

@alexreg

This comment has been minimized.

Contributor

alexreg commented Nov 4, 2018

@Nemo157 Good question. I think it should work, but it wasn't really a concern of my initial PR. I should be submitting another PR today, but after that focus can turn to various things. Do you fancy having a go at this issue? I could possibly advise.

Would be curious what @nikomatsakis thinks of this too.

@alexreg

This comment has been minimized.

Contributor

alexreg commented Dec 3, 2018

@withoutboats There are outstanding issues with trait aliases, but I think the first box can be checked in the original PR comment. The initial PR was merged some time ago, after all. :-)

@alexreg

This comment has been minimized.

Contributor

alexreg commented Dec 3, 2018

@Nemo157 Actually, would you mind opening a separate issue about that, which references this tracking issue?

@Nemo157

This comment has been minimized.

Contributor

Nemo157 commented Dec 3, 2018

@alexreg opened #56485

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment