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 upRFC: Generalized Type Ascription #2522
Conversation
Centril
added some commits
Aug 6, 2018
Centril
added some commits
Jan 3, 2019
haslersn
reviewed
Jan 5, 2019
| ### Expressions | ||
|
|
||
| The operational semantics and type checking rules for type ascription in | ||
| expression contexts is *exactly* as specified in [RFC 803]. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
graydon
commented
Jan 12, 2019
|
Approve, assuming nothing breaks (devil is always in details) this is just finishing a thing that ought to have been finished years ago and, as the doc mentions, many people and error messages already assume works this way. |
scottmcm
reviewed
Jan 15, 2019
| @@ -1067,57 +701,6 @@ to: | |||
| let : LET pat maybe_init_expr ';' ;` | |||
| ``` | |||
|
|
|||
| Finally, the grammar of function definitions is changed: | |||
This comment has been minimized.
This comment has been minimized.
scottmcm
Jan 15, 2019
Member
Seems like it's at least worth keeping some of this, right? Because the grammar is no longer pat:ty, it's pat, with a semantic requirement that it be ascribed at the top level?
This comment has been minimized.
This comment has been minimized.
Centril
Jan 15, 2019
•
Contributor
I changed it back to pat: ty. I'd love for the actual grammar to be pat and then do a semantic check since that makes it possible to improve error messages (by running full global inference) and it makes it possible to use it for proc macro attributes (this would be nice for #[proptest]).
Is @joshtriplett OK with that tho?
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern ref-binding-pat So I finally made time to read the RFC. Thanks @Centril for your attention to detail etc. Beautiful as ever. I'm going to post a few comments/concerns. Let me start with a nit: The discussion talks about the relative precedence of " |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis I think you are right; @haslersn helpfully pointed out the same error here. Happy to see we've come to the same conclusion; I'll revise the RFC in a bit... :) |
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern where-does-coercion-occur Next concern: the RFC is mostly silent, from what I can tell, on the topic of where coercions can occur. But we know it's very important. I think we should get to the bottom of this. In general, I'm open to an argument that this need not block the RFC and we can work it out during implementation, but I wanted to raise a few questions and see them noted somewhere. In the previous coercion RFC, we uncovered a soundness concern around references and coercions. I didn't find any direct mention of this concern in this RFC, though it does include a mention of temporaries and a reference to the old RFC that might've been alluding to the problem. But I think we should resummarize it. In a case like Similarly, this sort of case triggers coercions today: let x: T = ...But it's not clear to me if we intend for a case like this to trigger a coercion, I presume not: let Foo(x: T) = ...Allowing coercions on "inner type ascriptions" like this feels like a big "shift" to me in terms of how pattern matching works, since it would mean coercing "mid-match" -- I'm not even sure how this would work for matches with multiple arms: match foo {
Some(x: T) if ... => { /* do we coerce or not?? */ }
Some(x)=> { .. }
None => { ... }
}UPDATE: It occurs to me that another distinction of top-level type ascriptions from "inner ones" might have to do with rust's bidirectional type checking. We use top-level type ascriptions today as a kind of "hint" when checking the initializer expression for a let. Then we go and use the type of that expression when checking patterns. We would have to make a big shift here is we were going to use the "inner" type ascriptions in pattern matching in the same way -- and in fact I think it is incompatible with how the "binding mode" stuff works, since that relies on knowing the type of the value being matched to know when to insert implied |
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern what-do-ascripted-types-constrain It should be noted that we've actually implemented, as part of the NLL work, a somewhat generalized form of pattern type ascription similar to this. Doing so raised a number of interesting questions about just what these ascripted types actually mean. To start, a given But the ascription also serves as a declaration of the type of bindings within pat. So So the ascribed kind of propagates two ways: first, it bounds the expression, but then it propagates into the pattern as well, where it serves as a precise bound on the type of any bindings we encounter. Note that in some patterns, you may not have any bindings at all (e.g., But this raises some questions. Honestly I forget some of them, but here is an obvious one: What does it mean if there are two nested ascriptions? For example, I guess the second case is whether or not there are cases where we cannot determine the type of bindings given the outer ascription. I know we have some FIXMEs left in the code in this area. UPDATE: As noted in this comment, we also need to indicate how default binding modes affect things here. |
This comment has been minimized.
This comment has been minimized.
|
Note that the fn take_u32(_: u32) {}
let (x: u32, y: u32) = &(1, 2);
take_u32(x); // ERROR: expected `u32`, found `&{integer}`, consider dereferencingI don't think this is bad enough to convince me that default-binding-modes was a bad decision or that we shouldn't also have type ascription, but it's definitely a pretty confusing and unfortunate combination of features. |
This comment has been minimized.
This comment has been minimized.
|
I guess this is another indication of something that wasn't very well specified in the RFC. I had sort of assumed that let (x: u32, y: u32) = &(1, 2)would be an error, because it would imply that the type of But now that you mention it, it seems pretty important to clarify how the "binding modes" interact with both parts of the type ascription. I can't find the text now, but I feel like the RFC implied to me that the type ascription expected the |
This comment has been minimized.
This comment has been minimized.
To be sure I understand, you'd expect that all of the following would work? fn u32_refs(_: &[&u32]) {}
let (x: &u32, y: &u32): &(u32, u32) = &(1, 2);
u32_refs(&[x,y]);
let &(ref x: u32, ref y: u32): &(u32, u32) = &(1, 2);
u32_refs(&[x,y]);
struct T { x: u32 }
let T { x: &u32 }: &T = &T { x: 5 };
u32_refs(&[x]);
let &T { ref x: u32 }: &T = &T { x: 5 };
u32_refs(&[x]); |
This comment has been minimized.
This comment has been minimized.
|
@cramertj yes. specifically, when I see |
This comment has been minimized.
This comment has been minimized.
|
That seems at odds to me with |
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern confusing-code I basically think all of the drawbacks this RFC enumerates are very compelling, and overwhelm the motivation in my opinion. I am not in favor of allowing type ascription (at least, not with this syntax) in arbitrary expression and pattern positions. I would be in favor of removing the feature from nightly and "rescinding" the previous RFC. I'd add that in addition to the confusion around struct syntax that the RFC lists (as "sub-optimal experience in named fields"), I think the confusion that can arise when used in the middle of method chaining is also troubling: foo.iter().map(|x| x.bar()).collect(): Vec<_>.as_ref()I think everyone agrees that this code should be formatted differently, but I see it as a draw back that this RFC makes it valid at all. I would be in favor of extending type ascriptions to more specific locations that have these qualifications:
That is, I am in favor of adding type ascriptions in positions that don't have the drawbacks of generalized type ascriptions but also don't lead users to be very surprised that type ascription is not permitted in every position. Some positions that I think this applies to:
The headers of control flow constructs, in other words. |
This comment has been minimized.
This comment has been minimized.
|
I also don't think this RFC is of high enough priority to the Rust road map to devote a lot of attention to reaching consensus. If people don't feel devastated by it, I would propose accepting a subset like I just proposed under a new feature flag which we can probably reach consensus on quickly to stabilize and postponing further action. |
This comment has been minimized.
This comment has been minimized.
|
Unfortunately, my primary use cases for this are (a) “type debugging” and (b) disambiguation for generic function calls. And I can’t recall any time when I’ve wanted ascription in the positions you’ve mentioned. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jan 21, 2019
|
I'd think the Also, I'd think "trait ascription" might help address additional concerns, so |
This comment has been minimized.
This comment has been minimized.
|
@burdges @withoutboats Can you elaborate on whether your feelings are any different between patterns and expressions? I was pondering another possible subset where the bindings in patterns could be ascribed, since those are already slightly special (see |
This comment has been minimized.
This comment has been minimized.
comex
commented
Jan 21, 2019
I agree, but I think the solution is to remove the paragraph about making it valid; I don't think it's essential to this RFC. |
This comment has been minimized.
This comment has been minimized.
My concern with patterns is with struct patterns, where users will get weird behavior like trying to write
The reason you can't use turbofish is that the parameter is on the trait, right? That's been my experience, and its essentially always been |
This comment has been minimized.
This comment has been minimized.
Yep, I think that describes my experience as well, but
|
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jan 21, 2019
|
We could easily add a trait
that provided this functionality for type debugging. We should really have traits like this for asserts and things anyways. There is no substantial improvement in writing |
This comment has been minimized.
This comment has been minimized.
|
TBH, using identity for this feels like a bit of a hack to me. |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Jan 21, 2019
When partial turbofish finally arrives, this is a moot point though. I'm not sure of the status of this however. @Centril? |
This comment has been minimized.
This comment has been minimized.
comex
commented
Jan 22, 2019
That still would not allow pub trait Is<T> {}
impl<T> Is<T> for T {}
pub trait HackyIdentity: Sized {
fn identity<T>(self) -> Self where Self: Is<T> {
self
}
}
impl<T> HackyIdentity for T {} |
This comment has been minimized.
This comment has been minimized.
|
I still prefer generalized ascription, but one alternative that solves part of the motivation of this issue might be to allow turbofish to be used for trait methods. For example something like this (syntax is bikeshedable): trait Bar {
fn bar<T, U, V>(args: (T, U, V));
}
foo.bar::<as Bar, T, U, V>(args); |
Centril commentedAug 10, 2018
•
edited by scottmcm
This RFC supersedes and subsumes RFC 803. It generalizes existing type ascription in
letbindings to everywhere a pattern can occur and makes some changes to ascription in expressions. You may now for example write:Here, the underlined bits are patterns.
Finally, when a user writes
Foo { $field: $pat : $type }, and when$patand$typeare syntactically α-equivalent, the compiler emits a warn-by-default lint suggesting:Foo { $field: ($pat : $type) }.To @nrc, @kennytm, @varkor for reviewing the draft version.
To @scottmcm in particular for reviewing and being my rubber duck wrt. type inference.
Edit: Direct link to the pFCP checkboxes: #2522 (comment)