Initializing arrays, and const

I've found arrays-of-Options to be a little clunky:

struct Foo {
  name: String
}

fn main() {
  let arr: [Option<Foo>; 8] = [None; 8];
}

This doesn't work, because Foo needs to be Copy (according to the error).

I saw someone mention that a work-around is using const { None }:

struct Foo {
  name: String
}

fn main() {
  let arr: [Option<Foo>; 8] = [const { None }; 8];
}

This does indeed compile .. but why? What is it that the compiler can not figure out without the const { }?

1 Like

I assume, that this tells the compiler to evaluate the expression at compile time, i.e. the compiler knows at compile time, that you have a constant value of Option<Foo>::None in there and thus is doesn't need to to check any trait bounds on Foo, since it knows that Option<Foo>::Some(...) can never occur.
But a compiler specialist may give a more accurate answer than this.

Depending on your use case you may also want to have a look at MaybeUninit.

1 Like

The latter works, because the repeat operand of an array expression must be either Copy or a constant item:

Edit: As to why the const block is needed, None as an expression itself can't be promoted to a constant as is; every expression that can be promoted needs to be behind a reference.

10 Likes

To expand on the explanation:

As a rule of thumb

let arr: [Option<Foo>; 8] = [None; 8];

is basically equivalent to

let temp: Option<Foo> = None;
let arr: [Option<Foo>; 8] = [temp, temp, temp, temp, temp, temp, temp, temp];

and this only works for values that are Copy.

On the other hand

let arr: [Option<Foo>; 8] = [const { None }; 8];

is semantically equivalent to

const TEMP: Option<Foo> = None;
let arr: [Option<Foo>; 8] = [TEMP, TEMP, TEMP, TEMP, TEMP, TEMP, TEMP, TEMP];

which is equivalent to

let arr: [Option<Foo>; 8] = [None, None, None, None, None, None, None, None];
13 Likes

Is there a possibility of expanding the const promotion rules to encompass cases like these? Has there been any discussions I can read?

No, if anything the theme has been restricting const promotion further, now that there's const { … } to let you opt-in to the places where you want it.

2 Likes

It would still make sense to expand the const promotion rules (or the repeat operand rules):

The Rust Reference says in chapter 6.6:

A unit-like struct is a struct without any fields, defined by leaving off the list of fields entirely. Such a struct implicitly defines a constant of its type with the same name.

and in chapter 6.7:

Variant constructors are similar to struct definitions, and can be referenced by a path from the enumeration name, including in use declarations.

[…] Tuple-like and unit-like variants also define a constructor in the value namespace.

So unit-like structs and unit-like variants both should have a constant in the value namespace and behave like constant items when used as a value.

But in array repeat operands both don't behave like constant items and are only recognized as expressions.

This has the weird effect that

struct Foo; // not Copy
const ARR: [Foo; 8] = [Foo; 8];

fails to compile, but

struct Foo {} // still not Copy
const Foo: Foo = Foo {};

const ARR: [Foo; 8] = [Foo; 8];

compiles.

1 Like

Hmm, yeah, if the https://rust-lang.github.io/rfcs/1506-adt-kinds.html#unit-structs-style constants don't count, that might be worth making consistent.

1 Like