Convert primitive integer in generic const context

I want to to something like this:

However, this gives me a weird compilation error:

   Compiling playground v0.0.1 (/playground)
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
 --> src/lib.rs:1:12
  |
1 | #![feature(generic_const_exprs)]
  |            ^^^^^^^^^^^^^^^^^^^
  |
  = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
  = note: `#[warn(incomplete_features)]` on by default

error[E0207]: the const parameter `CAPACITY` is not constrained by the impl trait, self type, or predicates
  --> src/lib.rs:13:9
   |
13 | impl<T, const CAPACITY: u8> Prefixed<u8, ByteSizedVec<T, CAPACITY>> {
   |         ^^^^^^^^^^^^^^^^^^ unconstrained const parameter
   |
   = note: expressions using a const parameter must map each value to a distinct output value
   = note: proving the result of expressions other than the parameter are unique is not supported

For more information about this error, try `rustc --explain E0207`.
warning: `playground` (lib) generated 1 warning
error: could not compile `playground` (lib) due to 1 previous error; 1 warning emitted

I do not use Nightly too often, so I don’t know whether this is related to the instability of the feature.

Is it possible to compile-time cast a u8 to a usize in a const generic in Rust?

I want this to ensure that the size of the data fits into a u8 at all times.

A good way to think about the “is not constrained” error is that, in order to make use of an impl, the compiler has to be able to take a type — not a type alias, not a name for a type, but the type itself, as represented inside the compiler — and find out what impl blocks match that type.

But a const expression — here, { CAPACITY as usize } — is not in general invertible, so there's no way to go from the actual type heapless::Vec to your ByteSizedVec expression to determine whether a given heapless::Vec satisfies your conditions. The only way the compiler could support what you’re trying to do is either:

  • know how to invert an as expression and allow that specifically, or
  • expand your impl into 256 separate impls, one for each u8 converted to usize.

Neither of those is likely to happen. However, what you can do is replace your type with a struct, making ByteSizeVec be its own distinct type — this way, there is no need to consider heapless::Vec when trying to match your impl.

pub struct ByteSizedVec<T, const CAPACITY: u8>(heapless::Vec<T, { CAPACITY as usize }>)
where
    [(); CAPACITY as usize]:;

If you do that, then the impl can be defined. (It’s unfortunate that the where bound is needed since the expression can’t possibly overflow, but that’s the limitations of generic_const_exprs for you.)

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.