In the following code, why isn't the Wrap::new(2) expression evaluated at compile-time just like the Wrap::new(1) is?
struct Wrap(i32);
impl Wrap {
const fn new(value: i32) -> Wrap {
Wrap(value)
}
}
fn testing() {
let _arr = const {
// `Wrap::new(1)` is evaluated at compile-time.
let wrap = Wrap::new(1);
// Changing the literal `1` to some other value makes this
// fail at compile-time, proving that `Wrap::new(1)` was
// evaluated at compile-time.
assert!(wrap.0 == 1);
// `Wrap::new(2)` is not evaluated at compile-time.
// Must change the line to:
// [const { Wrap::new(2) }; 10]
[Wrap::new(2); 10]
};
}
In this case, the array repeat operand must be const because its type (Wrap) doesn't implement Copy. The code fails to compile because for some reason the const context (an inline-const in this case but that's not relevant) does not force the repeat operand to be evaluated at compile-time, even though a const context does force all expressions that are not repeat operands to be evaluated at compile-time.
An unfortunate consequence of this is that you have to write:
I suppose, one could try to complicate the rules, so e.g. expressions in positions that are guaranteed to be evaluated don’t need the explicit nested const { … }, but at least that does complicate the rules. I’m not even sure whether I’d find that’s a bad thing, arguably, the usefulness can outweigh the complexity [really, there’s not possibly any surprising behavior here, right?], but at least no-one has implemented any mechanisms for something like this yet.
I think, another thing to consider, which might also be reasonable to think from: coming from the other side whether perhaps we can improve the language to accept
At least the documentation should be changed because the Constant Evaluation chapter in the Rust Reference claims that "Certain forms of expressions, called constant expressions, can be evaluated at compile time. In const contexts, these are the only allowed expressions, and are always evaluated at compile time", and the last part of that sentence is not true because in const contexts, a constant expression that is an array literal's repeat operand is not always evaluated at compile time.
There’s probably improvement that can be made, but an array’s element is not a “const context” by that definition, because constant expressions aren’t the only allowed expressions. A Copy expression is also valid. (That is, whether or not something is a “const context” is not type-sensitive, nor does it depend on whether it is itself nested in a “const context”.)
I assume that by an "array's element" you mean an array literal's repeat operand (the expression before the ; token). It's true that a repeat operand's place is not a const context. But everything inside a const block is a const context, and because the array literal in the code in my original post is inside a const block, the array literal's repeat operand is also in a const context.
By the way, it seems that the term "array literal" is not used in the Rust Reference, so I should have used the correct term "array expression".
I think it'd make sense for outer const {} to work on array initializers.
I suspect the reason why it doesn't work isn't a grand deliberate design, but rather a consequence of arrays in Rust being const-ish long before the language had real const eval support.