rust

Trait associated constants in `const` expressions


Consider the following code:

use std::ops::Shl;
use std::marker::PhantomData;

trait Number: Shl<u8, Output=Self> {
    const ONE: Self;
}

impl Number for u8 { const ONE: Self = 1;  }
impl Number for u16 { const ONE: Self = 1; }
impl Number for u32 { const ONE: Self = 1; }
impl Number for u64 { const ONE: Self = 1;  }
impl Number for usize { const ONE: Self = 1; }

struct Bar<const OFFSET: u8>;
impl<const OFFSET: u8> Bar<OFFSET> {
    const SHIFTED: u32 = 1u32 << OFFSET;
}

struct Foo<N, const OFFSET: u8> (PhantomData<N>);

impl<N: Number, const OFFSET: u8> Foo <N, OFFSET> {
    const SHIFTED: N = N::ONE << OFFSET;
}

fn main() {
    let _bar: Bar<1> = Bar;
    let _foo: Foo<u32, 1> = Foo(PhantomData);
}

Playground

Here we have two structs Foo and Bar, while each is trying to calculate a value of 1 shifted by the number given as generic constant (OFFSET). However Bar's associated constant has a concrete type u32, while Foo is generic over numerical types implementing the << operation and having an associated constant that is equal to 1 of that specific type.

However, the Bar type is compiling, while Foo fails as the << operation can't be used in const context in this case:

   |
22 |     const SHIFTED: N = N::ONE << OFFSET;
   |                        ^^^^^^^^^^^^^^^^
   |
   = note: calls in constants are limited to constant functions, tuple structs and tuple variants 

I am not sure why the two cases are fundamentally different, as in both cases the operator is applied to constants. Is there a way to work around this?


Solution

  • Constants can be calculated from constants, but types and constants cannot be mixed. This is just not supported on the stable MVP of const generics.

    Is there a way to work around this?

    On nightly, you can enable #![feature(generic_const_exprs)] and it will Just Work. Beware: that feature is incomplete and not ready for production, as the compiler will warn you.

    On stable, a possible workaround is to use typenum, which simulates constants entirely with types.