rustcompile-time

const Iteration over values of an array


In Rust, it is possible to iterate over values of an array:

const COOL_NUMBERS: [i32; 4] = [1, 3, 3, 7];

fn do_something_cool() {
    for number in COOL_NUMBERS {
        // ...
    }
}

But it is not possible to do in const context (at least not on stable):

const COOL_NUMBERS: [i32; 4] = [1, 3, 3, 7];

const fn do_something_cool() {
    for number in COOL_NUMBERS {
        // ...
    }
}
error[E0015]: cannot convert `[i32; 4]` into an iterator in constant functions
 --> src/lib.rs:4:19
  |
4 |     for number in COOL_NUMBERS {
  |                   ^^^^^^^^^^^^
  |
  = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

...

See playground link for full list of errors.

I know that it is possible to do things like this to sidestep the error:

const COOL_NUMBERS: [i32; 4] = [1, 3, 3, 7];

const fn do_something_cool() {
    do_for_loop(&COOL_NUMBERS)
}

const fn do_for_loop(numbers: &[i32]) {
    match numbers {
        [] => {}
        [number, rest @ ..] => {
            // ...
            do_for_loop(rest);
        }
    }
}

But frankly, this looks horrible, isn't there a better way?

So, my question is:

  1. Is there a cleaner way to const-iterate over an array in stable?

  2. Is there at least a cleaner way to const-iterate over an array in unstable?

  3. Are there any crates that help with doing this?


Solution

  • After some more searching I found out that it's possible to do this in stable:

    const COOL_NUMBERS: [i32; 4] = [1, 3, 3, 7];
    
    const fn do_something_cool() {
        let mut i = 0;
        while i < COOL_NUMBERS.len() {
            let number = COOL_NUMBERS[i];
            // ...
            i += 1;
        }
    }
    

    If you need a crate to work with const Iterators, you may use konst. Here's an example from konst docs:

    use konst::iter::{ConstIntoIter, IsIteratorKind};
    
    struct Upto10(u8);
    
    impl ConstIntoIter for Upto10 {
        type Kind = IsIteratorKind;
        type IntoIter = Self;
        type Item = u8;
    }
    
    impl Upto10 {
        const fn next(mut self) -> Option<(u8, Self)> {
            if self.0 < 10 {
                let ret = self.0;
                self.0 += 1;
                Some((ret, self))
            } else {
                None
            }
        }
    }
    
    const N: u32 = {
        let mut n = 0u32;
        konst::iter::for_each!{elem in Upto10(7) =>
            n = n * 10 + elem as u32;
        }
        n
    };
    
    assert_eq!(N, 789);