-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: Permit _ in type aliases #2524
Conversation
Today, @Centril pointed out to me that Polymorphically (with respect to the type parameters of an enclosing function) asserting that a closure takes the same type in as out is currently not expressible (at least, in a local context). However, this RFC allows a way to do just that: let id = { type T = _; move |x: T| -> T { x } }; The semantics of In general, this feature lets us constrain closures better than we could before. In particular, I believe that the following are equivalent (up to technicality): run({ fn foo<T1: C1, ..>(x1: U1, ..) -> R { .. }; foo })
run({
type T1: C1 = _; // ..
move |x1: U1, ..| -> R { .. }
}); Now, I should point out that this is not the same as higher-ranked types (hence why I said above "up to technicality"). The example
where each use of If I think of more examples where introducing named inference variables is useful, I'll note them. |
I feel that this feature would confuse new users because of its similarity in use to existential types. Also, given that we don't have that much experience with existential types yet, I'm not convinced that listing all bounds on an existential type is unscalable. Can you provide examples of crates where this proved unscalable? |
Can you elaborate on this? If I were to write the documentation for Fundamentally,
This is not possible; Also note that if you have a situation such as: trait Avocado<T> { ... }
struct Tomato<U>(...);
impl<T: TraitA + TraitB, U> Avocado<T> for Tomato<U> { ... } and you want to create an existential type RedOnion<T: TraitA + TraitB>: Avocado<T> + all_the_other_traits; You have to hoist the type parameter of Worse still, consider that you have: existential type Salt<T>: Debug; In this case, it could be that existential type Salt<T>: Debug + Pepper if T: Lettuce; However, not even this would suffice in all cases because there may not be a bound With |
The RFC does not mention the semver problems associated with this feature. The reason
I also think that a viable alternative is to offer a lint which suggests to expose common traits like |
It does. See the last paragraph in the drawbacks.
Yep; that's the inevitable consequence. However, a tool such as semverver should be able to capture such breakage. The RFC is clear on that it is not a tool that should be used for encapsulation.
In the general case yes; but this assumes that you have exported the type alias publically (and that change is somewhat likely). If it is just an internal implementation detail, then you should be able to change such details if you don't rely on the difference between
That would realistically only work for libstd traits but not for user defined traits. All in all, I'm not sure I agree that this is a viable alternative. |
Why could there not be a lint for traits which have an associated type including Assuming the lints are there, I'm not sure if I'm pro or con this idea. I feel like we should get the experience with existential types first, then consider whether the overhead of using them is too high and something like this is necessary (along with looking at alternatives for existential types that allow for the conditional bounds you mention). |
I and @oli-obk discussed this a bit further on Discord, and I think that this lint would be reasonable and I'll change the RFC accordingly. (Do note the unresolved question which touches upon this...).
Again, note that proc macros could not, in the general case, make use of conditional bounds (which also come with their own complexities) because the macro has no way to determine up front what bounds to conditionally add (unless it just lists a bunch of traits, but that would be ad-hoc and then you have more or less regained the semver problems you were trying to be without..). Conditional bounds also have no way to leak inherent implementations, which could be useful. Comparatively, Wrt. experience, I think that we can experiment with both concurrently. |
The syntactic difference between the two is not very large, and the semantic difference is IMHO subtle. I don't think there are many people who would intuitively think that the difference is that one is opaque and one is transparent. I think you did a fine job explaining it in the "Guide" section of the RFC, but my worry is about somebody coming across
I'm not really convinced that these cases come up frequently enough in practice. I can only recall one time when I ever had that many bounds on a type. Sure, it was annoying, but given how uncommon it is, having a whole language feature for that seems like overkill. I would much rather wait and see if this turns out to be a problem in real code. |
To elaborate a bit further, I really want to avoid adding lots of subtle features to the type system. Learning rust's type system is already hard for many people, and I think the bar for adding new features should be rather high: there should be a demonstrated, wide-spread need for the feature in the ecosystem. I'm not denying that such a need may come, but I think we should wait a bit and see if people actually run into the problems motivating this RFC. |
I have to disagree here. While
That's fair enough; but I think the difference would be noticeable once you tried to use the constructs.
First, thank you :) With respect to being confused or attributing the wrong meaning to it, I think such a risk is low. If you already understand what The nice thing about having a stabilization period is that these hypotheses can be tested.
I don't think of this as adding a new feature to the type system. Rather, we would be extending a feature that already exists (see
I can only speak for myself, but I have a real world need for this today (the custom derive macro in the motivation) and |
I'd use this if it exited. Another idea is controlling the leakage, ala
Or strange things like
where |
This proposal would be very useful to me (motivation). However, I propose the restriction: inferred types and all derived types should not be allowed to be |
Opposed. Inference has already gone too far in the language with RFC 2071; the things that are impossible in the status quo are ok to remain impossible. They do not warrant further inference. The cognitive load of allowing / encouraging yet more inferred bindings is too high. |
Is there any particular reason you think the use cases (e.g. macros) are not well justified?
I understand the semantic versioning concerns people have, they are quite legitimate, wherefore this proposal needs some rework (along the lines of #2524 (comment)). Cognitive load seems different tho. I think this reduces cognitive load as |
Macros are an escape hatch that happens at an inopportune phase in the compiler, and as such they always run up against whatever the current limits of the language are. New non-macro language features should very rarely be motivated by making a particular macro easier to write: that way leads to unlimited growth. The analogy with local inference is incorrect, and users will largely have no idea how impl trait works, so analogy to aspects of its function (its opacity or lack thereof) cannot be leveraged and are not relevant. It's already beyond the cognitive limits of most users, as clearly evidenced by most discussion around it. |
@graydon I would like to be able to type things like the following but make fn ident_i32(x: i32) -> i32 { x }
struct S<F: Fn(i32) -> i32> {
f: F
}
let s: S<_> = S { f: ident_i32 }; Since closure / function types are impossible to express, some type of auto-derivation is needed to do this. Why I want to do this is complicated, and I'm not actually sure this is the best approach. See this example. Here, |
We talked about this in a lang team meeting today, and we'd like to postpone this until after @rfcbot close |
Team member @joshtriplett has proposed to close this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
Shouldn't this be disposition postpone then? |
I think so. As a matter of form I think the team member(s) should please say which specific "more places" |
type Foo = _; | ||
|
||
impl Iterator for Bar { | ||
type item = _; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type item = _; | |
type Item = _; |
Concretely, |
Thanks for that clarification. That seems to make sense to me. If this RFC is closed rather than postponed, then at that time, if it still seems appropriate, presumably the right thing to do would be to make a new RFC. |
Also, to clarify: this RFC is being closed rather than postponed not because we're necessarily rejecting the idea, but because it would need re-evaluation after that point, and a fresh RFC taking into account such changes. |
The final comment period, with a disposition to close, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC is now closed. |
🖼️ Rendered
📝 Summary
Permit type aliases and associated types to have their types be inferred in such a way that their nominal types be transparent as opposed to opaque like so:
You may also optionally constrain a type alias or associated type with a bound by writing
type Alias: Bound = <definition>;
. This provides the minimum capability of the alias as opposed to the maximum capability (see RFC 2071).💖 Thanks
To @varkor for reviewing the draft version of this RFC.