Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upType ascription #803
Conversation
nrc
self-assigned this
Feb 3, 2015
P1start
reviewed
Feb 3, 2015
| ``` | ||
| P ::= SP: T | SP | ||
| SP ::= var | 'box' SP | ... | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
P1start
Feb 3, 2015
Contributor
I think P vs. SP could be clarified here. pat: type is not a valid pattern (e.g., in match) today, and the only thing that resembles that syntax is let’s syntax. Is P supposed to represent what goes in between the let and = in let <P> = <value>;? If so, it should probably be clarified that P is not just a normal pattern.
P1start
reviewed
Feb 3, 2015
| In type checking, if an expression is matched against a pattern, when matching | ||
| a sub-pattern the matching sub-expression must have the ascribed type (again, | ||
| this check includes subtyping and implicit coercion). Types in patterns play no | ||
| role at runtime. |
This comment has been minimized.
This comment has been minimized.
P1start
Feb 3, 2015
Contributor
How does type ascription in patterns interact with let and function declarations? Presumably, let would now just be followed by a pattern (rather than a pattern optionally followed by a colon then a type), and the same for function parameters. Function parameters are required to have their types fully specified, but type ascription complicates this in that fn foo((x: i32, y: i64)) should be valid as well as the current fn foo((x, y): (i32, i64)). Presumably the rule simply needs to be adjusted to something like a pattern in a set of function parameters must have its type determinable without using type inference. That rule would allow some interesting cases that don’t even need type ascription like fn foo(NonGenericStruct(a, b), (), SingleVariantEnum), which may or may not be desirable.
This comment has been minimized.
This comment has been minimized.
nrc
Feb 9, 2015
Author
Member
I believe the last example is desirable. I'll update the RFC to clarify this and your previous point.
This comment has been minimized.
This comment has been minimized.
|
RFC probably should say that this replaces currently limited type ascriptions in |
kennytm
reviewed
Feb 3, 2015
| // With type ascription. | ||
| fn foo(Foo { a: i32, .. }) { ... } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
kennytm
Feb 3, 2015
Member
What would we get inside foo:
fn foo(Foo { a: Bar, .. }: Foo<Bar>) { ... }- A variable named
a, or - A variable named
Bar(the current behavior)?
This comment has been minimized.
This comment has been minimized.
nrc
Feb 9, 2015
Author
Member
Good question. I don't think there is a good answer. I think the least worst is to assume that users will prefer the common pattern of using the same name for both the name of the field and the new variable (fn foo(Foo { a, .. }: ... in the current syntax), then assume that a single : in a pattern always denotes a type, i.e., we assume Bar is always a type (this is backwards incompatible, as you allude to). If a user wants to rename the variable, then they'd have to use a : b : _, which is bad. I think the alternative is theoretically nicer - type ascription should be more optional, but less practical, since it is common to reuse the field name.
This comment has been minimized.
This comment has been minimized.
P1start
Feb 9, 2015
Contributor
I would say that it’s probably more common to want to rename the variable than to ascribe the type. After all, we don’t ascribe the type of any struct field patterns today (because we can’t), and that isn’t causing any major problems. The Foo { a, .. } notation is really just shorthand/syntactic sugar for Foo { a: a, .. }, so I feel it should have a lower priority than other more fundamental parts of the syntax. Foo { a: b: _, .. } (explicitly renaming) looks pretty bad and is not an obvious way of resolving the ambiguity, while Foo { a: a: Type } (explicitly type-ascribing) looks OK and is fairly obvious given that the shorthand is just optional sugar.
I think that Foo { a: b, .. } not working would be too surprising to be worth it, and the backwards-incompatibility also just makes matters worse. (Even better in my opinion would be to change struct initialisers to stop overloading :, but that’s already been discussed (in this RFC and elsewhere).)
This comment has been minimized.
This comment has been minimized.
nrc
Feb 9, 2015
Author
Member
Yeah, on second thoughts, this makes more sense. Avoiding the backwards incompatibility/code churn is especially desirable. I think I was over-estimating how often type ascription would be used.
This comment has been minimized.
This comment has been minimized.
CloudiDust
Feb 20, 2015
Contributor
While it is more common to want renaming than type ascription, I think Foo {x: x: Foo, y: y: Bar} is still a bit strange, would Foo {.x: Foo, .y: Bar} look better as a sugar? (If we don't change the value binding sigil to =>.)
This comment has been minimized.
This comment has been minimized.
|
Why not use an identifier and get around the similarity to struct initializers? |
This comment has been minimized.
This comment has been minimized.
|
I think type ascription is great, but I've never liked how everyone's always treated let z = if ... {
foo.enumerate().collect() :: Vec<_>
} else {
...
};Or anything else really. |
This comment has been minimized.
This comment has been minimized.
|
@blaenk How should this be parsed? let f = foo::bar::baz;Is it the path |
This comment has been minimized.
This comment has been minimized.
jsanders
commented
Feb 3, 2015
|
@blaenk: Do you think you find |
This comment has been minimized.
This comment has been minimized.
|
@sfackler fair enough, I completely forgot that paths also use @jsanders Yeah, very little width is one thing, and also because it's used for explicit typing. When I look at code quickly, at a glance, I have to stop to look at the context to determine if it's an explicit type on a variable, function argument, etc., or if it's a type ascription. It becomes ambiguous to me whether something is a definition or a use of an already defined item. Note that I'm saying it's ambiguous to me, not the compiler. Also I forgot to mention something that's mentioned in the drawbacks. If we ever --- and I think it's possible given previous discussions --- introduce named arguments, I would much rather have |
This comment has been minimized.
This comment has been minimized.
jsanders
commented
Feb 4, 2015
|
@blaenk: Interesting, I guess I don't think much about the distinction between definition and use. Changing |
This comment has been minimized.
This comment has been minimized.
|
Yeah, I definitely don't think anyone would have much of a problem when it concerns literals, like your example shows. However, my main concern was for example with function parameters, such as this one from the RFC: foo(x: &[_], y: &[_]);From a quick glance, because foo(x is &[_], y is &[_]);
let z = if ... {
foo.enumerate().collect() is Vec<_>
} else {
...
};Like I said I'm not trying to push But anyways, this is pretty subjective. I'm pretty flexible and I'm sure that I would get used to it without complaining, I just think it's something to think about right now before we go forward. Especially when we also consider if we would like My point is that using an alternative syntax to avoid potential confusion and to allow the possibility of using it for named arguments is insignificant compared to actually having type ascription at all, especially given the other drawback: "Interacts poorly with struct initialisers." |
This comment has been minimized.
This comment has been minimized.
|
since #601 (be -> become) is landing right now, "be" is free again and does not mean tail call anymore. I agree with @blaenk that "is" sounds too much like something returning a boolean due to a runtime-check (or compile-time in case of a generic?) |
This comment has been minimized.
This comment has been minimized.
|
My current feeling:
|
This comment has been minimized.
This comment has been minimized.
|
I agree with @nikomatsakis, only more so. I think that
(I also don't actually like the idea of using |
This comment has been minimized.
This comment has been minimized.
|
I feel like this is something that we do not have to do now or any time soon - it's a nice-to-have. |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 5, 2015
|
I don't think this is a bad feature, but making expressions more complicated has more costs than benefits. (In patterns, the proposal removes some complexity.) How many
Benefits
That may be more convenient but overall usability can suffer by making expressions stranger and by encouraging longer expressions. Sometimes you can just write a literal suffix like Costs
|
This comment has been minimized.
This comment has been minimized.
|
I'm in favour. I have a library where being able to write: let foo =
something.bitcast() : &[u8x16]
.convert() : &[u8]
.some_method();would be useful. The methods are defined like (I imagine this may apply to a generic On that note, my example is assuming that type ascription works inside method chains like that, which requires having |
This comment has been minimized.
This comment has been minimized.
|
@1fish2: I don't think that this is particularly worse than (or even very different from) the situation with My experience with statically typed languages with inference (particularly Haskell is relevant here) is that, even for applications that don't use much type ascription directly, there is a benefit to type ascription for debugging/pedagogy. If you have a type error in code that's using fairly generic functions, but you're not entirely familiar with how the types in a given API work together, you can sometimes use type ascription to figure out which function in the chain is returning something different from what you expect. That is, you can very quickly iterate with type ascription, in any arbitrary spots in a complex expression, to trigger a type error that's better than the one the compiler gave you at first, and/or to discover which step prevented type inference from resolving an ambiguity. Then you can easily yank the ascription back out of the code if you have a better fix, and it's not needed anymore. I also think that using type ascription improves readability significantly compared to the alternatives. To be frank, I would much rather have type ascription up front, with some of the weird details of UFCS syntax being the arcane you-should-learn-this-last feature, than to have no type ascription and rely on extra Type ascription is one of the simpler features to learn, and I would expect it to be something mentioned pretty early on in most tutorials and books. IMO, the existing workarounds have a much higher cost both for learners and for developers using Rust day-to-day. @huonw: I like that example. If it doesn't work, there's always this: let foo =
((something.bitcast() : &[u8x16])
.convert() : &[u8])
.some_method();Not so elegant, but possibly less annoying than adding two extra |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 5, 2015
|
Thanks, @quantheory ! That's informative, and those techniques ought to be in tutorials. Agreed, it's the volume of features that concerns me, not really this particular one. |
This comment has been minimized.
This comment has been minimized.
|
The alternatives mentioned (literal suffixes and @brson If we do this now maybe we have a chance to remove literal suffixes from the language? At the very least, using Maybe that's undesired, but I am feeling very confident about cleaning up those bits of syntax and semantics. |
This comment has been minimized.
This comment has been minimized.
|
+1 to using type ascription instead of literal suffixes. |
This comment has been minimized.
This comment has been minimized.
|
I'd rather have type ascription than literal suffixes, and I would rather change struct notation than lose type ascription. I didn't care about this feature until I had to work with traits... when writing tests especially you need to have the exact type of basically everything. I had a Working with closures is another example where I would like to just say
but I have to write THIS:
I can't omit even the length of the array, so when I add more stuff to it I have to manually fix it:
Type ascription is a huge ergonomics deal in current Rust until type inference is much better than what it is now |
This comment has been minimized.
This comment has been minimized.
Ditto to both. (I'd also rather change |
This comment has been minimized.
This comment has been minimized.
|
Do consider the ticking clock for 1.0. If we change the struct syntax now, it's backwards compatible to add type ascription post-1.0. (We could also not change struct initializers at all, at the cost of having to cut out any parts of this RFC that would cause an ambiguity.) If we remove literal suffixes, however, we would want type ascription immediately in order to replace them, which means that it would have to be implemented ASAP. Once you have type inference and type ascription for unsuffixed literals, literal suffixes are saving very little ergonomically. So I agree that in an ideal world we could just remove them as a redundant language feature. But I think it's up to the core team whether they think that they would be able to implement this RFC quickly enough to break backwards compatibility here. |
This comment has been minimized.
This comment has been minimized.
|
@quantheory this RFC exists because I implemented type ascription on a dare :). Only for expressions, but that seems to be what you're talking about. It would be tinier if it weren't for that weird |
This comment has been minimized.
This comment has been minimized.
|
@nrc I would prefer to cause it to avoid coercion in all |
This comment has been minimized.
This comment has been minimized.
|
The core team has decided to approve this RFC. The discussion began with a more wide-reaching proposal that included, among other things, type ascription for patterns, and was gradually simplified into its current form. Notable concerns that were raised and resolved include the following:
There is also a bit of ongoing discussion on how to handle type ascription in a ref context so as to preserve soundness. Although ensuring soundness is important, the precise scenario under discuss seems to be relatively minor and hence can be resolved as we go. (Note that, as with most RFCs, the intention is to leave the new syntax feature-gated until we've had more time to gain experience with it. In particular, this is not considered a must have for 1.0.) |
nikomatsakis
referenced this pull request
Mar 16, 2015
Open
Type ascription (tracking issue for RFC 803) #23416
This comment has been minimized.
This comment has been minimized.
|
Tracking issue rust-lang/rust#23416 |
nikomatsakis
merged commit e215e59
into
rust-lang:master
Mar 16, 2015
This comment has been minimized.
This comment has been minimized.
nrc
added a commit
to nrc/rfcs
that referenced
this pull request
Mar 18, 2015
nrc
added a commit
to nrc/rfcs
that referenced
this pull request
Mar 23, 2015
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis Question: How does this apply to match statements? I believe this would be a very useful case when matching on |
This comment has been minimized.
This comment has been minimized.
|
@GBGamer what you want (I believe) is type ascription in patterns (a pattern is the general version of the thing to the left of |
This comment has been minimized.
This comment has been minimized.
|
Cool, thanks :) On Tue, Mar 24, 2015 at 3:28 PM, Nick Cameron notifications@github.com
"Unjust laws exist; shall we be content to obey them, or shall we endeavor |
alexcrichton
referenced this pull request
Mar 26, 2015
Closed
Move the trivial_numeric_casts lint to allow-by-default #1020
niconii
referenced this pull request
Nov 18, 2015
Closed
Rust Needs The Ternary Conditional Operator (-?-:-) #1362
This comment has been minimized.
This comment has been minimized.
|
What's the state of this? |
This comment has been minimized.
This comment has been minimized.
|
No implementation.
|
This comment has been minimized.
This comment has been minimized.
|
Type ascription seems to be cursed - everyone going to rebase rust-lang/rust#21836 disappears immediately. |
This comment has been minimized.
This comment has been minimized.
vitiral
commented
Aug 17, 2016
•
|
I'm not sure what the process is supposed to be for this, but the syntax chosen for this has conflicted with other desireable language features, aka https://internals.rust-lang.org/t/pre-rfc-named-arguments/3831/103 Not necessarily that we should implement that feature, but the fact that it conflicts gives credence to how the syntax migh be confusing. I'm also having difficulty seeing how the (simple) example given in the RFC:
could not be accomplished with turbofish
Not that turbofish is the greatest syntax ever, but it is in std and serves a purpose. Isn't turbosh nearly orthogonal to this feature, and if so shouldn't this feature be removed? I am worried about additional syntax appearing in the std language, making things even harder to parse for developers. |
This comment has been minimized.
This comment has been minimized.
|
@vitiral
Changing it would put one more nail in type ascription's coffin.
There are cases when type hint can't be provided through generic parameters/arguments, e.g.
|
This comment has been minimized.
This comment has been minimized.
|
I'm not sure about "coffin"... type ascription doesn't really have a coffin. It's a really great feature, and we're not likely to try and support a special-cased and specialized feature (named parameters) over a very-much wanted and very useful feature (type ascription). |
This comment has been minimized.
This comment has been minimized.
jsgf
commented
Jun 24, 2017
•
|
What's the state of this feature? It seems to me that this would be very useful for futures-oriented programming. Because expressions of composed futures/streams get very complex types, it would be very useful to be able to "pin" parts of an expression to expected types, both to help type inference and to get better error messages. Current mechanisms to do this ( (ping @alexcrichton, @brson as it came up in conversation with them a couple of months ago) |
This comment has been minimized.
This comment has been minimized.
|
@jsgf |
nrc commentedFeb 3, 2015
•
edited by dtolnay
Closes #354
rendered