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

Tracking issue for Self and associated types in struct expressions and patterns #37544

Closed
petrochenkov opened this issue Nov 3, 2016 · 19 comments
Labels
B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-RFC-implemented Blocker: Approved by a merged RFC and implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@petrochenkov
Copy link
Contributor

I'm going to add a feature gate according to #37035 (comment), this is a corresponding tracking issue.

@crumblingstatue
Copy link
Contributor

crumblingstatue commented Nov 3, 2016

This doesn't feel complete to me without also allowing Self for tuple and unit-like struct, and enum expressions. It just feels very inconsistent being able to do fn new() -> Self { Self { ... } }, but not fn new() -> Self { Self(...) }, fn new() -> Self { Self }, and fn new() -> Self { Self::Variant }.

I was looking forward to this feature, so I don't have to repeat type names in as many places, but in its current state this is too inconsistent to be widely applicable.

@petrochenkov
Copy link
Contributor Author

petrochenkov commented Nov 3, 2016

Self::Variant (and Alias::Variant in general) is in the queue (#26264), it's just not implemented yet.

Self(..) for struct S(); or Self for struct S; would be some kind of new language entity, value alias (different from just a constant) referring to the original value - struct constructor. It's certainly can be hacked into the compiler, but I'd like to have some more foundation under it and see wider picture - how else these value aliases can be used, how they are related to possible pattern aliases, etc.

@crumblingstatue
Copy link
Contributor

Self::Variant (and Alias::Variant in general) is in the queue (#26264), it's just not implemented yet.

👍

Self(..) for struct S(); or Self for struct S; would be some kind of new language entity, value alias...

I don't want to get off-topic here, but I find it odd that a type alias does not give you the full capabilities that you can do when using the original type name.

When one says struct Foo, does that not only create a new type, but also some kind of special entity that allows value construction that is different than a type, and these two things are under the same name? Is there a fundamental reason why a type itself cannot be used for these purposes? If this is too complicated to answer here, that's okay. I don't want to hijack this thread too much.

@petrochenkov
Copy link
Contributor Author

@crumblingstatue

Is there a fundamental reason why a type itself cannot be used for these purposes?

It's possible, just messy.
Suppose, for example, that we have an expression

A::B(0, 1)

It may a function call, or it may be a tuple struct construction.
We need to resolve A::B to understand what is it.
Rust have two primary namespaces - one is for types, and the other is for values. This is pretty fundamental thing that can't be changed backward compatibly.
Functions are defined in value namespace and types like structs are defined in type namespace.

If the "type itself" is used for looking up tuple struct constructors, then we need:

  • Search A::B in both namespaces, this kind of contradicts to the point of namespaces.

  • Set up namespace priorities. What if both function A::B and struct A::B exist? Should we prefer one of them or report an error?

  • Ok, suppose we prefer a function. Now we need to resolve A::B in value namespace and if this resolution fails, then we need to resolve it again in type namespace.
    Name resolution happens in two phases:

    • Primary resolution pass, based only on names, used for everything except for associated items.
    • Resolution in type checking, used for associated items (method resolution, etc.), this jumps through various type checking hoops (trait selection, inherent impls, parameter substitution, autoderef for methods) to find the correct resolution.

    If A::B is not resolved in value namespace during the first pass, then we can't simply search type namespace, because A::B may be, for example, a method. We need to record partial resolutions in both namespaces and proceed to type checking when full resolution is completed and we select a function if it's found and a type otherwise.

Instead of doing all this, tuple/unit structs define additional function/constant-like items in value namespace which then used during resolution of A::B(0, 1), but can also be used in other ways like normal functions/constants. So, namespaces stay clearly separated and there's no need to disambiguate between types and values and keep both resolutions until type checking. But it's still possible to implement this fall back to types backward compatibly, even now.

sophiajt pushed a commit to sophiajt/rust that referenced this issue Nov 5, 2016
Feature gate Self and associated types in struct expressions and patterns

cc rust-lang#37544
Fixes rust-lang#37035 (comment)
r? @nikomatsakis
@nikomatsakis nikomatsakis added B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. T-lang Relevant to the language team, which will review and decide on the PR/issue. B-RFC-implemented Blocker: Approved by a merged RFC and implemented. labels Nov 14, 2016
@nikomatsakis
Copy link
Contributor

@rfcbot fcp merge

I propose that we stabilize this tracking issue. This is the feature which allows you to use Self and associated types in struct expressions. Note that it is slightly different to write impl Foo<T> { fn new() -> Self { Self { ... } } as to write impl Foo<T> { fn new() -> Self { Foo { ... } }. In particular, in the latter case, the Foo { ... } does not specify what the type parameter is -- it must be inferred (in this case, from the declared return type). That is not true for Self { ... }. I cannot imagine another semantics for this notation.

My main concern is that perhaps it is confusing to allow types (and not just structs) at all! I am open to the possibility of letting this bake a little longer and not moving to FCP.

@rfcbot
Copy link

rfcbot commented Nov 14, 2016

Team member @nikomatsakis has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@eddyb
Copy link
Member

eddyb commented Nov 14, 2016

I like this feature and even look forward to having it integrated with variant types!

@nrc
Copy link
Member

nrc commented Nov 15, 2016

My main concern is that perhaps it is confusing to allow types (and not just structs) at all!

This is my concern too, however, my understanding is that this ship sailed some time ago and we allow all sorts of things in the ident position of a struct literal.

@nikomatsakis
Copy link
Contributor

@nrc

This is my concern too, however, my understanding is that this ship sailed some time ago and we allow all sorts of things in the ident position of a struct literal.

Off hand I'm not sure what you mean... have an example? Do we allow e.g. type aliases?

@nrc
Copy link
Member

nrc commented Nov 20, 2016

@nikomatsakis We do allow type aliases and this is what I had in mind. FWIW, we also allow, e.g., Foo::<u32> { ... }. However, associated types in that position do not work.

@nikomatsakis
Copy link
Contributor

So in particular this does work:

struct Foo<T> { x: T }

type Bar = Foo<u32>;

fn main() {
    let x = Bar { x: 22 };
}

@withoutboats
Copy link
Contributor

@rfcbot reviewed

@petrochenkov
Copy link
Contributor Author

ping @nikomatsakis
I hoped to get this into the upcoming beta :(

@aturon
Copy link
Member

aturon commented Dec 20, 2016

@petrochenkov We can backport the stabilization. Looks like this was blocked on @pnkfelix who's been on vacation. I'll try to get a hold of him now.

@pnkfelix
Copy link
Member

@rfcbot reviewed

@rfcbot
Copy link

rfcbot commented Dec 20, 2016

🔔 This is now entering its final comment period, as per the review above. 🔔

psst @nikomatsakis, I wasn't able to add the final-comment-period label, please do so.

@nrc nrc added the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Dec 21, 2016
@rfcbot
Copy link

rfcbot commented Jan 23, 2017

The final comment period is now complete.

@nikomatsakis
Copy link
Contributor

@petrochenkov you want to prepare stabilization PR?

1 similar comment
@nikomatsakis
Copy link
Contributor

@petrochenkov you want to prepare stabilization PR?

bors added a commit that referenced this issue Jan 27, 2017
Stabilize Self and associated types in struct expressions and patterns

Rebase of #37734
Closes #37544
r? @nikomatsakis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-RFC-implemented Blocker: Approved by a merged RFC and implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants