As part of the Rust ergonomics initiative, I’m working on an RFC to allow type annotations on const
and static
items to be elided. I’d like to get some feedback on a few particular issues I’ve come across, as well as overall thoughts on the idea.
(note: there’s some previous discussion on this issue from before I was involved: https://github.com/rust-lang/rfcs/issues/1349)
Here’s the high-level plan: if a unique type can be inferred for a const/static declaration based only on the right-hand side of the declaration (including any known types for any variables/functions in the RHS), then the type can be elided. So the following would type check:
const GREETING = "Hello"; // infers to &'static str
fn get_greeting() -> &'static str {
GREETING
}
Unlike variables defined with let
, though, this would use only local type inference: it would not attempt to infer a type based on a later use of the item. 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
.
const THE_ANSWER = 42; // nothing in RHS indicates this must be i16
fn get_big_number() -> i16 {
THE_ANSWER
}
So, first issue: today, the only thing preventing closures (rather than merely references to closures) to be const/static items is that there’s no way for a programmer to write down their type. This change would remove that barrier. From a technical standpoint there seem to be no difficulties, but how would the type for closures be documented in something like rustdoc?
Second issue: AFAIK, this would be the first change that would allow items exported from a crate to have an inferred type. Without careful design, this could cause unintended issues. For example, say I have a crate that exports a constant FOO
with type i64
, but FOO
is never used in the crate itself. If I remove the type annotation on FOO
(and we assume the compiler infers some default type like i32
), I can compile the crate without error, but downstream consumers of my crate might get compiler errors.
From what I can tell, this is an issue only for numeric literals (but please correct me if I’m wrong). I’ve thought of three different options to deal with this, and I’d like the community’s feedback on them:
- Infer a type only if the type is completely determined by the right-hand side of the declaration. So
42i32
is fine, but42
is not. - If the item is exported, require that the type be completely determined by the RHS; otherwise just infer the default type for numerics (
i32
for integers,f64
for floats). This is more flexible than option 1, but determining whether a constant is exported is slightly complicated: for example, a non-exported constantBAR
could be used as the definition of exported constantFOO
. - Always default numeric literals to
i32
/f64
, regardless of context. This obviously incurs the problem described above, but perhaps the trade-off is worth it. A compiler warning would probably be a good idea here.
I’m curious what the community thinks about this, as well as whether there are other issues I haven’t thought of. Any and all feedback welcome!