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

Been running into the issue listed at static - How to specify const array in global scope in Rust? - Stack Overflow.

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? static - How to specify const array in global scope in Rust? - Stack Overflow

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];
3 Likes

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.

4 Likes

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.

1 Like

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.

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.

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:.

1 Like

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?

1 Like

I think the right token was ";" instead of ","

1 Like

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
1 Like

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: