Workaround for const_evaluatable_checked on stable?

I'm in a situation where I'd really like to use arithmetic with const generics to allow something like this:

fn foo<const N: usize>(input: [f32; N]) -> [f32; N/4] {
    ... 
}

But this requires the const_generics and const_evaluatable_checked features and I don't want to be relying on anything that triggers the incomplete_features lint. min_const_generics is in beta though, so I'm happy to use that.

Are there any workarounds on stable Rust where I can take a fixed-length array and return a new array which is 1/4 the length?

I tried adding another layer of indirection using traits and an associated const, but that didn't get me very far.

Example using associated constants
trait MultiplyBy4 {
    const VALUE: usize;
    type Array;
}

impl<const N: usize> MultiplyBy4 for [f32; N]
{
    const VALUE: usize = N*4;
    type Array = [f32; Self::VALUE];
}

Fails to compile with

error: generic `Self` types are currently not permitted in anonymous constants
  --> src/lib.rs:16:24
   |
16 |     type Array = [f32; Self::VALUE];
   |                        ^^^^^^^^^^^
   |
note: not a concrete type
  --> src/lib.rs:13:38
   |
13 | impl<const N: usize> MultiplyBy4 for [f32; N]
   |                                      ^^^^^^^^

Variations on this seem to fail with similar errors.

Would a signature like

fn foo<const N: usize>(input: [[f32; 4]; N]) -> [f32; N]

(or with [[f32; N]; 4] instead of [[f32; 4]; N] if that makes more sense) do the job?

(That won't work on stable because even min_const_generics has not hit stable yet. But if you're willing to use beta, or nightly without feature gates, it should be okay.)

Hmm... I didn't think of using a multi-dimensional array like that

The actual use case is in a "pipeline" where each stage will do some processing on the data, with each step looking something like this:

trait Foo<Input> {
    type Output;

    fn bar(&mut self, input: Input) -> Self::Output;
}

The previous step returns a [f32; 32] that this step needs to "condense" into a [f32; 4], but I wanted to make this operation work with more than just [f32; 32] because it'll pop up elsewhere.

I guess I could make the previous step return a multidimensional array instead. We're already using FFI to populate the array, so an extra transmute() won't cause much harm.

You can't transmute from or into an array of generic length unfortunately, see

I don't know what a good workaround for this would be in your case, maybe some additional raw pointer shenanigans.

FWIW, another option is not to use const_generics yet, and keep using the poor man's equivalent that most crypto libs use:

fn main ()
{
    use ::generic_array::{arr, typenum::consts::*};

    let _: [f32; 1] = lib::foo::<U4>([1., 2., 3., 4.].into()).into();

    let _: [f32; 1] = lib::foo(arr![f32; 1., 2., 3., 4.]).into();
}

mod lib {
    pub
    fn foo<N: ConstUsize> (input: Array_f32<N>)
      -> Array_f32<op!(N / U4)>
    where
        // [(); N / 4]:,
        N : ::core::ops::Div<U4>, op!(N / U4) : ConstUsize,
    {
        let fst = input[0];
        todo!()
    }

    …
}
2 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.