-
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
Const/static type annotation elision #2010
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
- Feature Name: const-static-type-elision | ||
- Start Date: 2017-04-29 | ||
- RFC PR: | ||
- Rust Issue: | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Allow type annotations to be elided on all const and static items with a unique | ||
type, including in traits/impls. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
In most cases, the type for constant and static items is obvious, and requiring | ||
a redundant type annotation is a papercut many programmers would want to avoid. | ||
For example, these two declarations would result in compiler errors in the | ||
current version of Rust: | ||
|
||
```rust | ||
const GREETING = "Hello, world!"; // unique type: &'static str | ||
static NUM = 42i32; // unique type: i32 | ||
``` | ||
|
||
This is usually no more than a small annoyance, but the risk involved in eliding | ||
the types also seems small. | ||
|
||
In the terms of | ||
[the ergonomics initiative blog post](https://blog.rust-lang.org/2017/03/02/lang-ergonomics.html), | ||
this change is broadly applicable, but the power is restrained by the | ||
limitations on type inference described below. | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
Rust would allow `const` and `static` items to elide type annotations and infer | ||
the type, but only if type inference can infer a unique type for the expression | ||
*before* applying any fallback rules. So if we have the following items: | ||
|
||
```rust | ||
struct S { | ||
a: i32 | ||
} | ||
|
||
const A: bool = true; | ||
const B: i32 = 42i32; | ||
const C: &str = "hello"; | ||
const D: S = S { a: 1 }; | ||
const E: [f32; 2] = [1.0f32, 2.5f32]; | ||
``` | ||
|
||
They could be written like this: | ||
```rust | ||
struct S { | ||
a: i32 | ||
} | ||
|
||
const A = true; | ||
const B = 42i32; | ||
const C = "hello"; | ||
const D = S { a: 1 }; | ||
const E = [1.0f32, 2.5f32]; | ||
``` | ||
|
||
To minimize the reasoning footprint, type elision would use only local type | ||
inference, rather than attempting to infer a type based on a later use of the | ||
item as with `let`-bound variables. For example, the following would result in a | ||
type error, because there are multiple possible types for the literal 42 | ||
(e.g. `i16`, `i32`, etc.), even though the use in `get_big_number` would require | ||
it to be `i64`. | ||
|
||
```rust | ||
const THE_ANSWER = 42; // nothing in RHS indicates this must be i64 | ||
|
||
fn get_big_number() -> i64 { | ||
THE_ANSWER | ||
} | ||
``` | ||
|
||
## Integer/Float Fallback | ||
|
||
The fallback rules (specifically, defaulting integer literals to `i32` and float | ||
literals to `f64`) are disallowed in cases where multiple typings are valid to | ||
prevent the type of an exported item from changing only by removing a type | ||
annotation. For example, say some crate exports the following: | ||
|
||
```rust | ||
const X: i64 = 5; | ||
``` | ||
|
||
If the developer later decides to elide the type annotation, then fallback would | ||
infer the type of `X` as `i32` rather than `i64`. If `X` is exported but not | ||
used within the crate, then this change could break downstream code without the | ||
crate developer realizing it. Admittedly, that scenario is unlikely, but | ||
ruling out fallback is the most conservative option and could always be added | ||
back in later. | ||
|
||
Fallback is acceptable, however, if the overall type is still unique even | ||
without the fallback rules, as in this example: | ||
|
||
```rust | ||
const fn foo<T>(_: T) -> char { 'a' } | ||
const X = foo(22); | ||
``` | ||
|
||
## Closures | ||
|
||
This design would allow closures (rather than just references to closures) to be | ||
used as `const`/`static` items, because the programmer no longer has to write | ||
down an inexpressible type. This shouldn't pose any particular difficulties from | ||
an implementation perspective, but it's worth being aware of. | ||
|
||
Documentation projects such as rustdoc may need to deal with this as a special | ||
case. @withoutboats | ||
[suggests](https://internals.rust-lang.org/t/pre-rfc-elide-type-annotations-from-const-and-static-items/5175/2?u=jschuster) | ||
coercing closures to fn types as one possible solution. | ||
|
||
# How We Teach This | ||
[how-we-teach-this]: #how-we-teach-this | ||
|
||
_The Rust Reference_ should record the rules for when the annotation is | ||
optional. _The Rust Programming Language_ and _Rust by Example_ should remove | ||
the sections that say annotations are required, and they may want to consider | ||
removing annotations from their examples of `const` and `static` items (see | ||
"Unresolved questions" below). | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
* Some users may find it more difficult to understand large constant expressions | ||
without a type annotation. Better IDE support for inferred types would help | ||
mitigate this issue, and a "best practice" of annotating the types on | ||
particularly complicated items where documentation is important should be | ||
encouraged. | ||
* Const functions in particular may make manually inferring a type | ||
difficult. Given | ||
[the original motivation for const functions](https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md), | ||
though, most uses in this context will likely be things like | ||
`AtomicUsize::new(0)` where the type is obvious. | ||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
* Allow numeric literals in const/static items to fall back to `i32` or `f64` if | ||
they are unconstrained after type inference for the whole expression, as is | ||
done with normal `let` assignments. If the constant is visible outside its | ||
crate but not used within the crate, this could change the constant's type | ||
without any warning from the compiler. That case is likely rare, though, and | ||
experienced Rust programmers would likely expect this kind of fallback, | ||
especially for simple cases like `const A = 42;`. | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
* Should _The Rust Programming Language_ remove the annotations used when | ||
introducing constants? |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Is it easy to enforce such rule? For instance
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.
Your assumptions are correct:
A
should type-check, butB
should not.According to my understanding of Rust's type inference mechanisms, this is always enforceable. Either the unification algorithm comes up with a unique type, in which case it's okay, or it doesn't, in which case the expression results in a type error. My understanding is limited, though, so I'd be happy to learn about any counterexamples.