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);
}
Here we have two struct
s 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?
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.