Search code examples
rustconst-generics

Create "axis" vectors as compile-time constants


Given a rather simple "small-vector" struct used for graphics or linear algebra:

pub struct VecN<T, const N: usize>
{
    v: [T; N],
}

How would one create the typical "axis" vectors as compile-time constants? I.e., something along the line of:

impl<T> VecN<T, 3> where T: From<f32> + Default
{
    const AXES: [VecN<T, 3>; 3] = [VecN::new([T::from(1.0), T::default(), T::default()]),
                                   VecN::new([T::default(), T::from(1.0), T::default()]),
                                   VecN::new([T::default(), T::default(), T::from(1.0)])];
}

(The above does not work since the From and Default traits cannot be evaluated at compile time.)

At best, the only "workaround" I've found is to create the constants for each type I'm interested in, e.g.,

impl VecN<f32, 3>
{
    const AXES: [VecN<T, 3>; 3] = [VecN::new([1.0, 0.0, 0.0]),
                                   VecN::new([0.0, 1.0, 0.0]),
                                   VecN::new([0.0, 0.0, 1.0])];
}

This feels very redundant, even if it can be alleviated pretty easily using macros. And more exotic types are of course not included. Is there some way to makes this work in a more generic way?


Solution

  • As you've mentioned, the problem here is that trait methods cannot be evaluted at compile time. However, what can be evaluated at compile time are trait constants, so you can define a trait with constants for ZERO and ONE, and use those instead:

    trait ConstNum {
        const ZERO: Self;
        const ONE: Self;
    }
    
    impl ConstNum for f32 { .. }
    impl ConstNum for f64 { .. }
    // etc
    
    impl<T: ConstNum> VecN<T, 3> {
        const AXES: [VecN<T, 3>; 3] = [VecN::new([T::ONE, T::ZERO, T::ZERO]),
                                       VecN::new([T::ZERO, T::ONE, T::ZERO]),
                                       VecN::new([T::ZERO, T::ZERO, T::ONE])];
    }