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 upPermit impl Trait in type aliases #2515
Conversation
varkor
added some commits
Aug 3, 2018
Centril
added
the
T-lang
label
Aug 5, 2018
varkor
referenced this pull request
Aug 5, 2018
Open
Tracking issue for `impl Trait` (RFC 1522, RFC 1951, RFC 2071) #34511
Nemo157
reviewed
Aug 5, 2018
| In addition, when documenting `impl Trait`, explanations of the feature would avoid type theoretic terminology (specifically "existential types") and prefer type inference language (if any technical description is needed at all). | ||
|
|
||
| ## Restricting compound `impl Trait` trait aliases | ||
| The type alias syntax is more flexible than `existential type`, but for now we restrict the form to that equivalent to `existential type`. That means that, if `impl Trait` appears on the right-hand side of a type alias declaration, it must be the only type. The following compound type aliases, therefore, are initially forbidden: |
This comment has been minimized.
This comment has been minimized.
Nemo157
Aug 5, 2018
Contributor
My biggest issue with this restriction is that it makes impl Trait inconsistent between type aliases and everywhere else (excluding the already inconsistent argument position). With existential type there was a simple rule that could be applied to impl Trait in every position except argument position: it introduces a new anonymous existential type in the current context. This rule works perfectly for return position, type declaration in bindings, type aliases, and could work for type declaration of struct/enum members if there wasn't a chance of confusion with argument-position-impl-trait.
This comment has been minimized.
This comment has been minimized.
varkor
Aug 5, 2018
Member
I'm not quite sure I follow your point. This restriction is simply a syntactic one: it is simply intended to sidestep the question of what:
type Foo = (impl Bar, impl Bar);means for now (because some people expressed unease at this construction in particular).
impl Trait continues to be applicable in exactly the same places as existential type: this rule simply means it can't be used in more complex scenarios than existential type yet.
This comment has been minimized.
This comment has been minimized.
Nemo157
Aug 5, 2018
Contributor
I mean that with existential types it's possible to have a single very simple rule for desugaring impl Trait that covers both:
type Foo = (impl Bar, impl Bar);
let foo: (impl Bar, impl Bar);but with type Foo = impl Trait; trying to apply the same sort of rule you get to this recursive definition that needs a special case when you use a bare impl Trait in a type alias.
This comment has been minimized.
This comment has been minimized.
varkor
Aug 5, 2018
•
Member
Ah, I see. Yes, you do have to have a single type alias as a base case if you're using the impl Trait type aliases to desugar. In practice, the desugaring of existential type is effectively replaced by the type alias. That is, the existential type design was originally intended to act as a desugaring for return-position and variable-binding (e.g. let) impl Trait. Using type aliases fills that role: but you can't use it to desugar itself. In practice, I don't think this is important, as there's no practical difference between the existential type itself and its alias.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
varkor
Aug 6, 2018
Member
Because if you try to desugar each occurrence of impl Trait you would end up trying to desugar:
type Foo = impl Trait;into:
type Foo = impl Trait;so you need to have this as a base case.
text/0000-impl-trait-type-aliases.md Outdated
text/0000-impl-trait-type-aliases.md Outdated
text/0000-impl-trait-type-aliases.md Outdated
text/0000-impl-trait-type-aliases.md Outdated
text/0000-impl-trait-type-aliases.md Outdated
text/0000-impl-trait-type-aliases.md Outdated
This comment has been minimized.
This comment has been minimized.
|
Just to clarify because I'm not 100% certain on how If I recall correctly, So technically I think the RFC should have some discussion of |
This comment has been minimized.
This comment has been minimized.
|
@clarcharr what's the intended meaning of where clauses on an |
This comment has been minimized.
This comment has been minimized.
|
So I just used In general, there are going to be things you can't express without |
This comment has been minimized.
This comment has been minimized.
|
Thanks, that clarifies things a lot for me (I think putting a bound directly on In the end I think I decided that that specific case acts identically to I'm trying to think of useful bounds that can't be written currently, I guess there could be something like Relatedly I was playing round with the current implementation and noticed that it allows specifying bounds in the type parameter list but not adding a where clause, that allowed me to come up with an example of a currently compiling existential type Bar<T: IntoIterator>: Iterator<Item = <T::Item as IntoIterator>::Item>;
fn bar<T>(a: T) -> Bar<T>
where
T: IntoIterator,
T::Item: IntoIterator,
<T::Item as IntoIterator>::Item: Clone,so even if |
This comment has been minimized.
This comment has been minimized.
|
One thing that I also realised is that this could be accomplished by trait aliases:
So maybe |
This comment has been minimized.
This comment has been minimized.
|
Sorry, I'll get the bounds question soon, but in the meantime, I've noticed that this RFC has a major incompatible flaw with the original RFC 2071 and it could potentially mean this syntax suggestion is misinformed. The current implementation of As such, Note that, contrary to some of the original stated motivations, this feature means that |
This comment has been minimized.
This comment has been minimized.
I did a quick mobile grok of #2071. Is it the reference section you're talking about where the existential type can be used as its resolved type within its declaring module? What's the tradeoff we're making by excluding this transparency from our model here? |
This comment has been minimized.
This comment has been minimized.
That doesn't seem inconsistent to my mental model actually. I've been thinking of |
This comment has been minimized.
This comment has been minimized.
That's right. If we include the transparency, it's inconsistent with
Yes, it's quite subtle. It could be consistent with argument-position, return-position and module-position |
This comment has been minimized.
This comment has been minimized.
Ah right, I'd totally forgotten about let mut x: impl Debug = {
if a { 1 }
else { some_fn_returning_i32() }
};
x = 1; // err: expected `impl Debug` got `i32` |
Centril
reviewed
Aug 17, 2018
text/0000-impl-trait-type-aliases.md Outdated
oli-obk
referenced this pull request
Aug 22, 2018
Merged
52985 diagnostics no concrete type behind existential type #53588
This comment has been minimized.
This comment has been minimized.
|
I'd like to propose something that might be a bit controversial. We should at least discuss whether we would like to mark the site where the abstract type and the concrete type meet: type Baz = impl Bar;
fn foo() -> as Baz {
some_other_fn()
}I think it would be beneficial because it makes the non-obvious obvious - in particular when the definition of the abstract type isn't right next to the function. Edit: I just chose the Edit2: It would also make this code (Playground) compile because one could specify which function "sets" the abstract type. |
oli-obk
referenced this pull request
Aug 26, 2018
Merged
Implement associated existential types #52650
This comment has been minimized.
This comment has been minimized.
Can we please stop using the nomenclature existential types? An existential value is a promise that there exists at least one such value. This is not what I suggested the syntax Note that I'm taking a very different view to what @MajorBreakfast wrote above — i.e. Could we please allow direct usage within type expressions? E.g. struct S {
f: impl Fn() -> i32
}
let v: Vec<impl T>; |
This comment has been minimized.
This comment has been minimized.
|
@dhardy Because of APIT and RPIT struct S {
f: impl Fn() -> i32
}Either an unnamed nominal hidden type (what’s the best shorthand if we can’t use existential?) existential type S_f: Fn() -> i32;
struct S {
f: S_f
}Or an unnameable generic type parameter (useless, but consistent with APIT): struct S<S_f: Fn() -> i32> {
f: S_f
} |
This comment has been minimized.
This comment has been minimized.
|
Why was APIT ever allowed in the first place? It doesn't add anything (other than a little sugar) and it's confusing, as seen here. Can we not restrict it to function arguments? |
This comment has been minimized.
This comment has been minimized.
|
I think with good documentation on why it’s treated like return position instead of argument position allowing |
This comment has been minimized.
This comment has been minimized.
How do we talk about variable bindings like
How about type elision? Or indirect typing or unspecified types. |
Centril
added
the
I-nominated
label
Sep 6, 2018
Centril
removed
the
I-nominated
label
Sep 20, 2018
This comment has been minimized.
This comment has been minimized.
|
@varkor Skimming over the RFC text as written here, I don't see any code snippets showing examples of existentials that have generic type/lifetime parameters... For example, RFC 2071 has the following example of code it alllows: #[derive(Debug)]
struct MyStruct<T: Debug> { inner: T };
existential type Foo<T>: Debug;
fn get_foo<T: Debug>(x: T) -> Foo<T> { MyStruct { inner: x } }Could you please expand the RFC text to include an example like this, to make it clear that the (I do understand that this RFC is just proposing a new syntax for an existing feature. But its still a good idea to try to provide examples of the basic cases that arise of the new syntax. I'd be especially happy if you went so far as to provide code snippets that use |
This comment has been minimized.
This comment has been minimized.
|
I do note now that the text does include the line:
So clearly the intent is to allow such things. But I'd still prefer they get a couple code snippet examples. |
Centril
added
revisit-after-2018-edition
and removed
revisit-after-2018-edition
labels
Sep 20, 2018
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Sep 24, 2018
|
This is looking in a pretty good state now to me. When can we think about FCPing it? |
varkor commentedAug 5, 2018
Allow
impl Traitto be used in type aliases, resolving the open question in RFC 2071 as to the concrete syntax forexistential type. This makes it possible to write type aliases of the form:Rendered.
Thanks to @rpjohnst and @Centril for their ideas, discussion and feedback.