Search code examples
arraysrustconstantscompile-timerust-no-std

Const array from range


How can I generate a long const array with values taken from a range? Bonus: 1) working with no_std, 2) without using any crates

What I would like to do:

struct A {
    a: i32,
    b: B
}

enum B {
    One,
    Two
}

const AS: [A; 1024] = [A{a: 1, b: B::One}, A{a: 2, b: B::One}, ..., A{a: 1024, b: B::One}]

The furthest I got so far is:

macro_rules! generate_list {
    ($($a:expr)+, $variant:expr) =>  {
        [ $( A { a: $a, b: $variant } )* ]
    }
}
const AS: [A; 1024] = generate_list!(1..1025i32, B::One);

At least one problem with this seems to be that the macro is evaluated before the range expression is expanded to a list of literals.


Solution

  • I'm not sure if its idiomatic, but perhaps the following might work for your use case:

    #[derive(Debug, Copy, Clone)]
    pub struct A {
        a: i32,
        b: B,
    }
    
    #[derive(Debug, Copy, Clone)]
    pub enum B {
        One,
        Two,
    }
    
    const fn gen_array<const N: usize>(offset: i32, b: B) -> [A; N] {
        let mut res = [A { a: offset, b }; N];
        
        let mut i = 0;
        while i < N as i32 {
            res[i as usize].a = offset + i;
            i += 1;
        }
        
        res
    }
    
    const AS: [A; 42] = gen_array(0, B::One);
    
    fn main() {
       println!("{:#?}", AS);
    }
    

    Playground

    I used a while loop, as a for loop is currently not allowed within a const context as E0744 indicates.

    The above would generate 42 values for AS with an offset of 0:

    [
        A {
            a: 0,
            b: One,
        },
        A {
            a: 1,
            b: One,
        },
        ...
        A {
            a: 41,
            b: One,
        },
    ]
    

    The Godbolt Compiler Explorer shows that it generates the same instructions as writing it out manually.