1

I can describe the dimension of this "circular" hexgrid..

circular hex grid

.. with only 1 value n defined at compile time:

const GRID_RADIUS: usize = 3;

Therefore, the number of cells in the grid is also known at compile time, since it is (2n+1)^2-n*(n+1) (here 37).

However, the following:

const N: usize = 3;
const N_CELLS: usize = ((2 * N + 1) ^ 2) - N * (N + 1);

struct Cell;

struct Grid {
    cells: [Cell; N_CELLS],
}

Does not compile:

error: any use of this value will cause an error
 --> src/main.rs:2:34
  |
2 | const N_CELLS: usize = ((2 * N + 1) ^ 2) - N * (N + 1);
  | -----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
  |                        |
  |                        attempt to subtract with overflow
  |
  = note: `#[deny(const_err)]` on by default

I understand that rustc worries that subtracting usize types may result in overflow, but I can guarantee that N_CELLS will always be positive in this case.

How can I take responsibility for this and have rustc trust me?

iago-lito
  • 3,098
  • 3
  • 29
  • 54

1 Answers1

6

There's no guarantee to be made — constant values are evaluated at compile time. The compiler knows if a value overflowed because it performed the calculation.

I understand that rustc worries that subtracting usize types may result in overflow, but I can guarantee that N_CELLS will always be positive in this case.

How can I take responsibility for this and have rustc trust me?

You cannot guarantee this (and the compiler shouldn't trust you) because you are incorrect. ^ means XOR, not "to the power of". The compiler executed your code and literally subtracted below zero, triggering the error. This is not hypothetical:

((2 * n) ^ 2) = 4
n * (n + 1) = 12
4 - 12 = -8
fn main() {
    let n: usize = 3;
    let n_cells: usize = ((2 * n) ^ 2) - n * (n + 1);
}
thread 'main' panicked at 'attempt to subtract with overflow', src/main.rs:3:26

In fact, thanks to this question, Rust now shows the values that cause the overflow, hopefully making the original mistake clearer :

 --> src/lib.rs:2:24
  |
2 | const N_CELLS: usize = ((2 * N + 1) ^ 2) - N * (N + 1);
  | -----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
  |                        |
  |                        attempt to compute `5_usize - 12_usize` which would overflow
  |
  = note: `#[deny(const_err)]` on by default

See also:

Unfortunately, you cannot currently use pow in a constant:

const N: usize = 3;
const N_CELLS: usize = ((2 * N + 1).pow(2)) - N * (N + 1);
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
 --> src/lib.rs:2:24
  |
2 | const N_CELLS: usize = ((2 * N + 1).pow(2)) - N * (N + 1);
  |                        ^^^^^^^^^^^^^^^^^^^^

You will have to expand the multiplication yourself:

const N_CELLS: usize = {
    let a = 2 * N + 1;
    let b = N * (N + 1);
    a * a - b
};
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Wops. Yes, I am incorrect. Sorry for that. I read that `core::num::::pow is not yet stable as a const fn`, but it works fine with `(2 * N + 1) * (2 * N + 1)` instead. – iago-lito Apr 13 '20 at 15:51