Currently the type ascription feature uses the : syntax. It’s on nightly for quite a long time, but it doesn’t look it’s going to be stabilized soon. I guess that one of the problems with the : syntax is its interoperation with struct literals, which also use the same symbol. So I’d like to propose to change the colon symbol to type to avoid this amiguity. I’ve tried to search whether it was discussed before, but I haven’t found anything. So if somebody has some links to previous discussion on this syntax, I’d be happy to see them.
Motivation
Less ambiguity with the struct literal syntax
More compatibility with various “anonymous structs” / struct shorthand syntax / keyword arguments proposals (by freeing the :).
Ability to use type ascriptions in patterns in an unambiguous way (I hope).
Examples
Type ascription of simple expression:
let foo = (0..10).collect() type Vec<_>;
Struct literals:
Struct { foo: foo type T } // simply annotating expression
Struct { foo type T } // shorthand syntax
Struct { foo type T: foo } // perhaps we should also allow this, for consistency?
Interaction with -> T-like functions (imho it reads pretty well):
let x = line.parse()? type i32;
let x = height.into() type f64;
Patterns:
match "aaa".parse() {
Ok(a type T) => { ... }
Err(Error { inner: e type E }) => { ... }
}
Detailed design
For expressions, use the same grammar as the as-conversion.
For patterns, not sure about the precise rules yet.
Drawbacks
Longer syntax (one may say it’s less perl-y though)
Perhaps it’s less discoverable? (although more searchable)
If we allow type in all patterns, there will be multiple ways to write an annotated let statement:
let a: T = foo();
let a = foo() type T;
let a type T = foo();
// Also
let b type Result<T, _>: Result<_, E> = foo();
(I guess that allowing current type ascription syntax – : – in patterns whould just change the let statement grammar to let <pat> [ = <expr>])
Probably, this also introduces ambiguity in grammar, which I just haven’t thought about yet.
I think in the type vs struct literal collision, struct is at fault. Everywhere else in the language it's binding: type, but in struct literals it's binding: value.
So if any such huge late change is going to be made, I suggest fixing struct literal syntax instead. It could be same as C:
This will remove the problematic type/value confusion from the syntax. It also makes it more greppable for .field_name = value which finds both obj.field_name = value.
Syntax is not the reason why the feature is in limbo (also there’s no conflict with struct literals in the current grammar).
The reason is that the feature is under-implemented (coercions) and is not generally useful enough.
Implementing coercions (while keeping soundness) will certainly move the feature closer to the final decision.
Implementing type ascriptions in patterns as an experiment on nightly will also move things in the right direction, I think.
IMO the motivation section of the type ascription RFC is really weak, and since it still hasn’t been stabilized after all this time, it seems to me like Rust works just fine without it. It’s at best a slight convenience in some cases, whereas it restricts the design space for other features (e.g. named arguments). Can’t we just scrap type ascription altogether?
Also; I’d prefer it if we did not scrap type ascription. To me, it provides a lightweight syntactic mechanism to guide and constrain type inference without turbofish or let bindings and can improve writing flow.
In my view, <expr> type <type> does not read well either while <expr> typed as <type> would, but that is simply too long and verbose.
Nooo! It's the only solution we have been promised to the Into/AsRef problem!... a problem which is only going to become more and more prominent over time:
The Into/AsRef problem
The author of crate bbbb, being the model Rustacean that he is, writes impl From<aaaa::A> for B rather than having from_a method.
The author of crate cccc, being the model Rustacean that he is, writes impl From<bbbb::B> for C rather than having from_b method.
Maybe we could just have a generic always-inline identity function that returns its argument, and use that for type ascription with no language changes:
id::<B>(x.into()).into()
Only difference seems to be that it doesn’t implicitly work for non-movable lvalues, but you need to borrow and dereference explicitly.
Turbo fish like this doesn't work in this particular instance because the parameter is on trait Into<T> not fn into(self). But there are several other solutions that don't require this type ascription feature.
I prefer to use from in cases where inference gets confused: C::from(B::from(func()). It doesn’t have the nice method call ordering but it’s rare to have more than one layer of them in my experience, and it matches my preferred String::from as well.
Isn’t struct literal syntax stable? In that case, it’s not going to change.
(And, to be honest, I don’t like that C-style syntax, it’s harder to type (= is less convenient to access on the keyboard than :) and harder to read too.)
This! I've been programming in Rust so far without once needing to touch type ascriptions. Turbofish and binding annotation are more than enough. Also ascriptions are ug-ly.
This also frustrates me when refactoring - moving let bindings into a struct for example. Have to change all the symbols. And it’s the syntax most functional languages use for fields.
I remember @nrc actually tried to propose changing the struct syntax pre-1.0 - would be cool to see it come up again as something to change across epochs, but I’m not sure it would have enough drive to get through.