Const Generics - how to ensure that usize const is > 0

Viewed 106

Given sample struct:

struct S<const N: usize> {
    arr: [u32; N] // I want size to be N > 0
}

I want to have N > 0.

In other words, I want to limit available values of N to be from range 1...MAX_USIZE, and not 0...MAX_USIZE.

2 Answers

To my knowledge it's not possible to add such constraints to const generics as of now, a workaround is a constructor that asserts N > 0:

struct S<const N: usize> {
    arr: [u32; N] // I want size to be N > 0
}

impl<const N: usize> S<N> {
    const fn new(arr: [u32; N]) -> Self {
        assert!(N > 0, "n must be greater than 0");
        S {
            arr
        }
    }
}

const FAILS_TO_COMPILE: S<0> = S::new([]);

Playground

results in:

error[E0080]: evaluation of constant value failed
  --> src/lib.rs:7:9
   |
7  |         assert!(N > 0, "n must be greater than 0");
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         the evaluated program panicked at 'n must be greater than 0', src/lib.rs:7:9
   |         inside `S::<0_usize>::new` at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/panic.rs:57:9
...
14 | const FAILS_TO_COMPILE: S<0> = S::new([]);
   |                                ---------- inside `FAILS_TO_COMPILE` at src/lib.rs:14:32
   |
   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)

In non-const contexts, this is a runtime error.

I would recommend assert!()ing in the constructor, like @sebpuetz said, however it is possible to do that. This is bad (see this answer of mine for an explanation why, and why this is a hard problem in general), but you can use associated items for that:

struct S<const N: usize> {
    arr: [u32; N],
}

impl<const N: usize> S<N> {
    fn new(arr: [u32; N]) -> Self {
        _ = <Self as AssertGt0>::VALID;
        
        Self { arr }
    }
}

trait AssertGt0 {
    const VALID: ();
}

impl<const N: usize> AssertGt0 for S<N> {
    const VALID: () = assert!(N > 0);
}

fn main() {
    // _ = S::new([]); // This fails compilation
}

Playground.

You cannot do that on where clauses as far as I know, you have to use a constructor.

Related