Rust `cannot use constants which depend on generic parameters in types`

Viewed 158

The full code playground

I am trying to write a data-structure such that it is generic over different dimensionality.

The beginning is:

/// A field of adjacent sections in a given dimensionality.
///  With 1, you have `|left|center|right|`.
///  With 2 dimensions you have:
///  ```
///    up_left│    up│up_right
///  ─────────┼──────┼──────────
///       left│center│right
///  ─────────┼──────┼──────────
///  down_left│  down│down_right
///  ```
///  Etc.
#[derive(Debug)]
struct Adjacents<T, const D: u32>([T; size(D)])

const fn size(d: u32) -> usize { 3u32.pow(d) as usize }

When trying to write implementations like:

impl<T> Adjacents<T,1u32> { /*...*/ }

I'm encountering the severe warning (in a few instances):

warning: cannot use constants which depend on generic parameters in types
  --> src/main.rs:26:9
   |
26 | impl<T> Adjacents<T,1u32> {
   |         ^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(const_evaluatable_unchecked)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>

In my use case certain functionality can be generic across the stored data type (T) but not generic across dimensionality (D), so it seems only inevitable I must use Adjacents<T,1u32> throughout.

Not to mention D does not depend on T (how could I convey this to the compiler?)

What am I missing here?


Full code here:

#![feature(generic_const_exprs)]

use std::ops::Index;

const fn size(d: u32) -> usize {
    3u32.pow(d) as usize
}

/// A field of adjacent sections in a given dimensionality.
///  With 1, you have `|left|center|right|`.
///  With 2 dimensions you have:
///  ```
///    up_left│    up│up_right
///  ─────────┼──────┼──────────
///       left│center│right
///  ─────────┼──────┼──────────
///  down_left│  down│down_right
///  ```
///  Etc.
#[derive(Debug)]
struct Adjacents<T, const D: u32>([T; size(D)])
where
    [(); size(D)]:;

#[allow(unused)]
impl<T> Adjacents<T, 1u32> {
    pub const LEFT: usize = 0;
    pub const CENTER: usize = 1;
    pub const RIGHT: usize = 2;
}
#[allow(unused)]
mod d1 {
    pub const LEFT: usize = 0;
    pub const CENTER: usize = 1;
    pub const RIGHT: usize = 2;
}
impl<T, const D: u32> Index<usize> for Adjacents<T, D>
where
    [(); size(D)]:,
{
    type Output = T;

    fn index(&self, index: usize) -> &Self::Output {
        self.0.index(index)
    }
}

/// A trait for functionality which can exist in many different dimensions, but requires explicit implementation for each dimension.
pub trait Border<const D: u32> {
    fn border(x: usize) -> Self;
}

/// We want to form a structure where the center is full and the borders are only full where they are adjacent to the center.
/// In 1d like:
/// ```
/// // [[None,None,Some(T)],[Some(T),Some(T),Some(T)],[Some(T),None,None]]
/// ```
/// (by using a trait and impl this allows us to do this for given any depth)
impl<T: Border<1u32>> Border<1u32> for Adjacents<Option<T>, 1u32> {
    fn border(x: usize) -> Self {
        Self(match x {
            d1::LEFT => [None, None, Some(T::border(d1::RIGHT))],
            d1::CENTER => [
                Some(T::border(d1::LEFT)),
                Some(T::border(d1::CENTER)),
                Some(T::border(d1::RIGHT)),
            ],
            d1::RIGHT => [Some(T::border(d1::LEFT)), None, None],
            _ => unreachable!(),
        })
    }
}
#[derive(Debug)]
struct Tp(usize);
impl Border<1u32> for Tp {
    fn border(x: usize) -> Self {
        Self(x)
    }
}

fn main() {
    let field: Adjacents<_, 1> = Adjacents([1u32, 2u32, 3u32]);
    println!("left: {}", field[d1::LEFT]);
    // [[None,None,Some(Tp)],[Some(T),Some(T),Some(T)],[Some(T),None,None]]
    let border: Adjacents<Option<Adjacents<Option<Tp>, 1u32>>, 1u32> =
        Adjacents::border(d1::CENTER);
    println!("{:?}", border);
}
0 Answers
Related