Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upTracking issue for trait aliases #41517
Comments
nagisa
added
B-unstable
B-RFC-approved
T-lang
labels
Apr 24, 2017
This comment has been minimized.
This comment has been minimized.
|
I think #24010 (allowing aliases to set associated types) should be mentioned here. |
added a commit
that referenced
this issue
Jun 5, 2017
added a commit
that referenced
this issue
Jun 19, 2017
added a commit
that referenced
this issue
Jul 7, 2017
Mark-Simulacrum
added
the
C-tracking-issue
label
Jul 22, 2017
This comment has been minimized.
This comment has been minimized.
|
I'd like to take a crack at this (starting with parsing). |
This comment has been minimized.
This comment has been minimized.
|
I read the RFC and I saw a call out to 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 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: |
This comment has been minimized.
This comment has been minimized.
phaazon
commented
Nov 3, 2017
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? |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
phaazon
commented
Nov 3, 2017
|
I see. Cannot we just use free variables for that? Like, |
This comment has been minimized.
This comment has been minimized.
|
@phaazon I don't understand this proposal. |
added a commit
that referenced
this issue
Dec 13, 2017
added a commit
that referenced
this issue
Dec 13, 2017
added a commit
that referenced
this issue
Dec 13, 2017
added a commit
that referenced
this issue
Dec 14, 2017
added a commit
that referenced
this issue
Dec 14, 2017
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Something I mentioned in the RFC: |
This comment has been minimized.
This comment has been minimized.
|
We can put a check for that in AST validation. However I suppose it could
be useful for code generation if there's no special case, I dunno.
…On Tue, Feb 27, 2018 at 12:48 PM, Clar Roʒe ***@***.***> wrote:
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.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#41517 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAC3n3HdG3ZOmbN2PKky7nFnx0WokY7Lks5tZD_fgaJpZM4NGzYc>
.
|
This comment has been minimized.
This comment has been minimized.
|
One other thing to note as a general weirdness is macro expansions involving trait bounds. Currently, But then, is |
This comment has been minimized.
This comment has been minimized.
|
Empty bound lists (and other lists) are accepted in other contexts as well, e.g. |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
I believe that in practice there shouldn't be a visible difference between So, in this case, |
This comment has been minimized.
This comment has been minimized.
|
@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.). |
This comment has been minimized.
This comment has been minimized.
|
I assume you mean The latter will work as expected. Although the former basically, what you're asking is the meaning of what |
This comment has been minimized.
This comment has been minimized.
|
@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.) |
This comment has been minimized.
This comment has been minimized.
|
anecdata, but I would find |
This comment has been minimized.
This comment has been minimized.
|
We actually did go back and forth on whether to use the keyword Would it make it better if you could The important thing is to get it implemented so we can experiment with these things. |
This comment has been minimized.
This comment has been minimized.
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.
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 |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
phaazon
commented
Aug 6, 2018
Yes, trait Foo { /* … */ }
trait Bar = Quux + Zoo + 'static;
Using the current terminology, 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:
|
This comment has been minimized.
This comment has been minimized.
|
@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.
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.
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.
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. |
This comment has been minimized.
This comment has been minimized.
|
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 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).
This is very like the argument that "users can get pretty far with an intuitive understanding of 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.
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: 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 |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
|
@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 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
~~~~~~~~~~~~~~~~~~~~~~~ boundAnd in that case, wouldn't it a lot more sense to talk about "constraint aliases"? |
This comment has been minimized.
This comment has been minimized.
Yep, copy & paste coding fail, thanks for pointing it out! I fixed it now. |
This comment has been minimized.
This comment has been minimized.
|
As for |
This comment has been minimized.
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();
// …
} |
This comment has been minimized.
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> |
This comment has been minimized.
This comment has been minimized.
Correct, good point! Internally in the compiler, things like So "bound alias" would probably be the correct terminology, but TBH I find that it sounds rather awful.
This is universal quantification. ;) But yes,
These are not constraints/where predicates though -- |
This comment has been minimized.
This comment has been minimized.
The terminology comes from Haskell where I think folks should take a look at Data.Constraint. It might be a useful experience to understand that stuff. |
nikomatsakis
referenced this issue
Nov 2, 2018
Open
Tracking issue for `trait alias` implementation (RFC 1733) #55628
This comment has been minimized.
This comment has been minimized.
|
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
Is this the intended design, or just a current implementation limitation? |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@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. :-) |
This comment has been minimized.
This comment has been minimized.
|
@Nemo157 Actually, would you mind opening a separate issue about that, which references this tracking issue? |
withoutboats commentedApr 24, 2017
•
edited by nikomatsakis
This is a tracking issue for trait aliases (rust-lang/rfcs#1733).
TODO:
Unresolved questions: