[Solved] Why can't the number of elements of const array be inferred?


#1

Been running into the issue listed at https://stackoverflow.com/questions/23810032/how-to-specify-const-array-in-global-scope-in-rust.

Specifically:

const NUMBERS: [i32] = [1, 2, 3, 4, 5];
error[E0308]: mismatched types
 --> src/main.rs:2:28
  |
2 |     const NUMBERS: [i32] = [1, 2, 3, 4, 5];
  |                            ^^^^^^^^^^^^^^^ expected slice, found array of 5 elements
  |
  = note: expected type `[i32]`
  = note:    found type `[i32; 5]`

error[E0277]: the trait bound `[i32]: std::marker::Sized` is not satisfied
 --> src/main.rs:2:28
  |
2 |     const NUMBERS: [i32] = [1, 2, 3, 4, 5];
  |                            ^^^^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `[i32]`
  |
  = note: `[i32]` does not have a constant size known at compile-time
  = note: constant expressions must have a statically known size

error: aborting due to 2 previous errors

error: Could not compile `rust-test`.

Is there a way to avoid counting the number of elements without adding a performance penalty as is shown in the answer? http://stackoverflow.com/a/23810557/1398841

Is the reason for this requirement just to keep things explicit and consistent? I could imagine a possible:

const NUMBERS: [i32; ..] = [1, 2, 3, 4, 5];

When should `const` be used within a function?
#2

There are a few times in Rust where we chose the most conservative option, because we weren’t comfortable deciding how much convenience is a good idea. const/static declarations are one of them: we don’t infer types or let you elide lifetimes at all.

This might be a good change, but it would have to go through all of the other design work for features that we usually do.

Note that you’re not using an array here: you’re using a slice. [i32] isn’t an array, [i32; N] for some N is.


#3

One nice thing about the explicit length is in helping you notice when you have made a breaking change to the public API of your crate. Suppose you have code like:

pub const NUMBERS: [i32] = [1, 2, 3, 4, 5];

Adding an element doesn’t feel like a breaking change because the type hasn’t changed:

pub const NUMBERS: [i32] = [1, 2, 3, 4, 5, 6];

And yet you have broken downstream code that looks like this:

let _: &[i32; 5] = &johnthagen::NUMBERS;
error[E0308]: mismatched types
 --> <anon>:6:24
  |
6 |     let _: &[i32; 5] = &johnthagen::NUMBERS;
  |                        ^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 5 elements, found one with 6 elements
  |
  = note: expected type `&[i32; 5]`
  = note:    found type `&[i32; 6]`

If you want the flexibility of changing the number of elements, use &[i32] as suggested by the StackOverflow answer. If not, the number of elements should be part of the type.


#4

Just like recently “'static” can be inferred, supporting a syntax like:

const NUMBERS: [i32, _] = [1, 2, 3, 4, 5];

Is sometimes handy. Where you prefer the “double booking” of specifying the array length, the older explicit syntax is still available.


#5

We have the sttatic_lifetimes (hope I recall the name correctly) feature that enables lifetime elision for static and const items. It’s likely to hit stable in 1.15.


#6

Thanks everyone for all of the great insights.

Rust is an awesome language, and I very much appreciate the team taking the time to do things right! So thank you :slight_smile:.


#7

Given:

const NUMBERS: [i32, _] = [1, 2, 3, 4, 5];

The rust playground shows:


rustc 1.19.0-nightly (f89d8d184 2017-05-30)
error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `,`
 --> <anon>:1:20
  |
1 | const NUMBERS: [i32, _] = [1, 2, 3, 4, 5];
  |                    ^ expected one of 7 possible tokens here

error: aborting due to previous error(s)

So is this actually true?


#8

I think the right token was “;” instead of “,”


#9

I think that @leonardo was just proposing an example of how this could look, even with ; it does not work on playground:

rustc 1.17.0 (56124baa9 2017-04-24)
error: expected expression, found `_`
 --> <anon>:2:26
  |
2 |     const NUMBERS: [i32; _] = [1, 2, 3, 4, 5];
  |                          ^

error: aborting due to previous error

#10

I would just like to advertise my new crate, build_const which lets you declare arrays like so:

let values: Vec<u8> = vec![1, 2, 3, 36];
consts.add_array("ARRAY", "u8", &values);

Not your question, but if you need that kind of thing use my crate! :slight_smile: