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 (ascription of patterns) #354
Comments
nrc
added
the
postponed
label
Oct 6, 2014
This comment has been minimized.
This comment has been minimized.
|
Generalizing the struct Foo<T> { a: i32, b: T }
let foo = Foo { a: 1, b: 2.0 };
let Foo { a: x, b: y: f32 } = foo; |
This comment has been minimized.
This comment has been minimized.
|
@bjz the identifiers in the shorthand |
japaric
referenced this issue
Nov 2, 2014
Closed
Allow type ascription on patterns and expressions #10502
This comment has been minimized.
This comment has been minimized.
|
See also some discussion on rust-lang/rust#10502 |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 1, 2015
|
What's the compelling reason for these features? I can see how type ascription could enable safer patterns, but in expressions it seems like excess syntax sugar and complexity. Benefits
The alternative is to write an ordinary Costs
Rust aims to be a production language that's predictable and reliable for security-critical and safety-critical software. Be careful about adding features from research languages like Haskell and Scala. |
This comment has been minimized.
This comment has been minimized.
Rust has tons of features from 'research languages', and is all the better for it, especially when it comes to safety and security. |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 1, 2015
|
Indeed. Borrow the ideas that have proven out. |
This comment has been minimized.
This comment has been minimized.
|
There is an implementation in rust-lang/rust#21836 |
This comment has been minimized.
This comment has been minimized.
Type annotation/ascription is one of the oldest ideas in programming languages. |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 1, 2015
|
@darinmorrison for anonymous expressions? And proven worthwhile? Do say more. Google mostly turns up info like the Scala Style Guide:
Criteria for language features should include usability, high bang/buck for the ecosystem, and not interfering with future plans. Apparently, type ascription for expressions is rarely used in Scala and accomplishes no more than using two orthogonal features together.
That seems to be a declaration for a |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Feb 1, 2015
|
@1fish2 I asked about something like this on IRC several months ago. The solution suggested to me was to add a macro to accomplish typing expressions. If we don't have support for it in the language, people will add a macro for it to individual projects, and all your arguments about confusion still apply or are made worse (naming and syntax could vary across codebases. An obvious alternative would be a standard macro that supplies the functionality, but I'm not sure if that's really a better choice than adjusting the syntax. Also, I'd be careful to note that your example with a struct is not necessarily something we expect (unless I'm a fool and those internal struct members are somehow expressions). As @huonw mentioned, we'll need to figure out if that makes sense (as a separate concern from being able to apply |
This comment has been minimized.
This comment has been minimized.
|
@1fish2 Yes. The idea is about as old as types themselves. It may not have been common in some of the most popular languages but that doesn't have any bearing on its utility. One strong argument in favor of allowing inline annotations is that it opens up options for making the type system more flexible, and potentially simplifying it even. If Rust ever adopts a bidirectional typing algorithm internally (I don't know what it currently uses), having these annotations will likely be important. (Some detail here and here). Top-level annotations are not always natural to use and may not even be feasible in some cases. Advocating for keeping the language simple and avoiding unnecessary features is admirable but I don't see this one as being a problem, at least not in the sense you have suggested so far. |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 1, 2015
|
+1. Go for a standard macro. That avoids the problems and costs. Programmers can handle a great deal of complexity so it takes much restraint to avoid adding little features one by one that build up enormous complexity and sharp edges, like C++. Usability death by a thousand additions. |
This comment has been minimized.
This comment has been minimized.
|
I have seen enough people being confused about I have a hard time finding PL features that aren't as equally represented (or more so) than type ascription, for most of your "Costs" bullet points, aside from the last 3 which are a single point. I am actually curious what languages use |
This comment has been minimized.
This comment has been minimized.
|
-1. Using I feel like the Rust syntactic space is already becoming over-crowded, and it will be difficult to find room for future language extensions. Please avoid adding new symbolic operators unless absolutely necessary. |
This comment has been minimized.
This comment has been minimized.
|
And no, macros are not a solution. You cannot create a // Original implementation (bad for inference and lifetime scopes):
{
let xs: Box<[_]> = Box::new([$($x),*]);
slice::SliceExt::into_vec(xs)
}
// New implementation:
<[_] as slice::SliceExt>::into_vec(Box::new([$($x),*]))
// Slightly better if we ignore the temporary Box::new usage:
<[_] as slice::SliceExt>::into_vec(box [$($x),*])
// And type ascription:
(box [$($x),*]: Box<[_]>).into_vec()Now you tell me what's more complicated and damaging. Another data point: if we decide to replace pub fn noop_fold_where_clause<T: Folder>(
WhereClause {id, predicates}: WhereClause,
fld: &mut T)
-> WhereClause {...}
// With this nicer version:
pub fn noop_fold_where_clause<T: Folder>(WhereClause {id, predicates}, fld: &mut T)
-> WhereClause {...}Now that I think of it, that may not be the best example - |
This comment has been minimized.
This comment has been minimized.
jmesmon
commented
Feb 1, 2015
|
@dgrunwald you bring up a good point: using If we could go back in time, it might make sense to avoid that mixed meaning and use (say) Off topic, but: Going forward, I don't think introducing more users of the "has value" meaning for |
This comment has been minimized.
This comment has been minimized.
|
@jmesmon I believe As for the current use in struct literals, I've got mixed feelings: // Out of these two, I find the JS(ON)-like syntax most pleasing:
Point { x: x, y: y }
Point { x = x, y = y }
// But here, using equal signs conveys the intention better:
Point { x: x: f32, y: y: f32 }
Point { x = x: f32, y = y: f32 } |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 1, 2015
|
There's a substantial forum discussion about struct initializer syntax where that's on topic. |
This comment has been minimized.
This comment has been minimized.
|
@eddyb: Using |
This comment has been minimized.
This comment has been minimized.
I think using match $input { a => { let x: $type = a; x } }Although this may not actually give the same behaviour. |
This comment has been minimized.
This comment has been minimized.
|
@huonw that's a nice way of solving the lifetime issue, but it's still useless for coercions. |
This comment has been minimized.
This comment has been minimized.
|
Oh geez. Type ascription has been in the plans since near-forever, only put off because it's backwards compatible, non-critical and can be added after 1.0. I've always been bothered by others not being bothered by the obvious syntactic conflict with it that (For what it's worth (which, post-alpha, is zilch), my preferred syntax for |
This comment has been minimized.
This comment has been minimized.
|
I'd never seen that syntax before in the context of Rust and I kinda like it, I wish we had this discussion before the alpha (is it really too late for miracles?). Point { .x = 10, .y = 11 }
// would expand to the more verbose
{ let p: Point; p.x = 10; p.y = 11; p }
// even if that would be safe, I doubt it will be overused as the
// struct literal syntax is always shorter - except if this worked:
{ let p: Point; (p.x, p.y) = (10, 11); p }
// but why not
Point { (.x, .y) = (10, 11) }We can still change this... right? |
This comment has been minimized.
This comment has been minimized.
|
Which leads to a further extension - if we had HashMap { ["foo"] = bar }
// as sugar for:
{ let m = HashMap::new(); m["foo"] = bar; m } |
This comment has been minimized.
This comment has been minimized.
bluss
commented
Feb 2, 2015
|
If you bring up named arguments, let's unify struct constructors with that. struct Point { x: i32, y: i32 }
let p = Point(x = 1, y = 2); |
This comment has been minimized.
This comment has been minimized.
I wish so, but since the alpha I've seen three reasonable syntax change RFCs (full disclosure: two were my own) summarily closed. Have any been accepted?
This is #372 :)
Yeah, C# 6 added this as well. That's actually where I got the idea for |
This comment has been minimized.
This comment has been minimized.
|
#372 would be a pain to analyze or trans, otherwise we would have it already. There's no real grammar issues there AFAICT, just wish we had a proper MIR... |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 2, 2015
|
Nobody has explained why type ascription on expressions is desirable for Rust. How many Language design should design for usability and do usability testing. It's easy for one person to add a feature and know what it does; not easy for all future programmers to come across yet another feature in code or docs and find out what it does and how it interacts with other features. Guido van Rossum on "Language Design Is Not Just Solving Puzzles":
|
This comment has been minimized.
This comment has been minimized.
I think they have. But here it is in a nutshell: Rust doesn't have global decidable type-inference. That means that sometimes annotations or hints are necessary. Without having inline annotations, you have to use something more elaborate like these macros with let or match, which may not always work due to language semantics (@eddyb pointed out one instance of this), or may change the meaning of your program in subtle unintended ways. Annotation should be a no-op (erased to the underlying term). There's plenty of other examples of why they are useful both in theory and practice. Adding type annotations to the language would hardly turn Rust into Scala or Haskell. From my perspective, if you really believe that this would be a harmful feature, the burden should be on you to justify your position with something concrete. |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 2, 2015
|
I don't actually think this proposal is terrible but I don't see the gain. Can you explain it an engineer? Please do give those examples. What concerns me is usability and learnability are under duress from a wave of feature proposals (and we need a production language that can replace C/C++ for safety-critical and security-critical software). I'm sorry, brother @darinmorrison but I tried to read your paper and had no idea what the math notation meant or how it relates to this proposal. Does "global decidable type-inference" mean a type probability cloud collapses over the whole program, yielding an eigen-type? It seems to me that limited, local inference would be more predictable and reliable. (Is there a doc on Rust type inference?)
How would adding a
I think I have. The proposal adds complexity and associated costs. It interferes with syntax for struct init and (future) default arguments. We could also do a quick usability test by showing examples to programmers and asking them to interpret. |
This comment has been minimized.
This comment has been minimized.
If type inference is decidable, it just means that for any program If type inference were global, it would try to do this across all parts of the program, even without top-level annotations on functions. This is like what the ML folks do but not how Rust works.
Well, yes. And Rust does not try to do global inference as far as I understand. But even if you restrict to some sort of local inference, it still does not mean you can avoid annotations or hints entirely. |
This comment has been minimized.
This comment has been minimized.
|
@1fish2 Rust currently allows specifying the type of a thing using I don't know Scala, and have no idea what connotations the feature might carry there. The syntactic conflict with |
This comment has been minimized.
This comment has been minimized.
1fish2
commented
Feb 2, 2015
|
Thanks. Here's a Scala example of type ascription:
now
or
or
which is clearer and just as concise. Anyway, for the
instead of
I don't think type ascription is a terrible thing but why pay all those costs to make a rare case more compact? The part that worries me is the drive to add features. |
nrc
added a commit
to nrc/rfcs
that referenced
this issue
Feb 3, 2015
This comment has been minimized.
This comment has been minimized.
|
See #803 |
nikomatsakis
closed this
in
#803
Mar 16, 2015
This comment has been minimized.
This comment has been minimized.
|
Reopening since #803 does not cover pattern ascription. And edited the issue title to reflect that. |
nrc
reopened this
Mar 16, 2015
nrc
changed the title
Type ascription
Type ascription (ascription of patterns)
Mar 16, 2015
nrc
added
the
T-lang
label
May 15, 2015
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 2, 2016
|
Some people have commented that they would like to see more examples of why type ascription would be useful. I can't speak for all cases - in particular I don't really get fn main() {
let connection = PgConnection::establish(DB_URL).expect(&format!("Error connecting to {}", DB_URL));
use schema::companies::dsl::*;
use models::*;
let results = companies.load::<Company>(&connection).expect("error loading models");
for company in results {
// what is the type of company?
}
}I think that for company: Company in results {
}Unfortunately, that's not allowed at the moment. To a newcomer to Rust, the above seems like something you should be able to do. Why? Because you can do it for normal let statements: // this compiles fine
let company: Company = something_that_has_type_company;So why can't you do it in a if let Some(company: Company) = results.into_iter().next()In the nature of making the language more consistent, and to allow these types of sanity checks, we should allow type annotations like this on all the |
This comment has been minimized.
This comment has been minimized.
Most people do something like let () = company;which will error as long as |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Nov 4, 2016
|
@steveklabnik You're right, you can do that, and for the above example I could have done something similar: for company in results {
{
let c: &Company = &company;
}
// ...
}But why can't you just put the type you're expecting into the for loop? Again, it should be consistent. |
This comment has been minimized.
This comment has been minimized.
|
Another example: |
petrochenkov
referenced this issue
Jun 11, 2017
Merged
Match Ergonomics Using Default Binding Modes #2005
lfairy
referenced this issue
Jul 30, 2017
Closed
`@let` and `@for` should support type annotations #73
pnkfelix
referenced this issue
Aug 12, 2018
Merged
MIR: support user-given type annotations on fns, structs, and enums #53225
This comment has been minimized.
This comment has been minimized.
|
Closing in favor of #2522. |
nrc commentedOct 6, 2014
Post-1.0, we would like to allow arbitrary type ascription - that is annotating any expression with a type. E.g.,
let _ = foo(x, y: Bar<int>, z);(type ascription on the sub-expressiony).Detail to be nailed down - precedence (probably the same as
as).Optional extra - type ascription on patterns, e.g.
let (x: Bar<int>, y) = foo(...);- useful when you care about the type of some part of the pattern but not others, especially when the bits you don't care about are_.Optional, optional extra - using the ascribed types in pattern matching for downcasting (an extension of some #349 proposals).