Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const arrays in rust - initialise with list comprehension?

Tags:

constants

rust

Is there a way to use closures or list comprehension when defining constant arrays in Rust? Suppose we have these three const arrays:

const K1 : [i32;8] = [100, 100, 101, 102, 104, 106, 108, 900];
const K2 : [i32;8] = [-100, -100, -101, -102, -104, -106, -108, -900];
const K3 : [i32;8] = [-900, -108, -106, -104, -102, -101, -100, -100];

In python K2 and K3 could be expressed as

K2=[-x for x in K1]
K3=[-x for x in K1[::-1]

Rust has the cute crate which emulates python list comprehension, but I don't think it can be used to define constants. So is there a more elegant solution than simply typing out K2 and K3 as above?


1 Answers

Someday, when Rust has more constant-evaluation abilities, we may be able to write the following:

const K1: [i32; 8] = [100, 100, 101, 102, 104, 106, 108, 900];
const K2: [i32; 8] = K1.map(|x| -x);
const K3: [i32; 8] = { let mut a = K2; a.reverse(); a };

However, for now, neither array::map() nor slice::reverse() are const fns, so you can't use them in constants. map in particular is going to be more trouble because it requires the ability to define higher-order const fns which is not yet available.

However, we can define our own const functions to do the specific jobs we need. This requires a lot more code, so it's not a great idea if you have only one case, but it could be worthwhile anyway to help readability of the constant definitions, or if these situations come up more than once.

const K1: [i32; 8] = [100, 100, 101, 102, 104, 106, 108, 900];
const K2: [i32; 8] = array_i32_mul(-1, K1);
const K3: [i32; 8] = array_reverse(K2);

const fn array_i32_mul<const N: usize>(factor: i32, mut a: [i32; N]) -> [i32; N] {
    let mut i = 0;
    while i < N {
        a[i] *= factor;
        i += 1;
    }
    a
}

const fn array_reverse<T: Copy, const N: usize>(mut a: [T; N]) -> [T; N] {
    let mut i = 0;
    while i < N / 2 {
        let from_end = N - i - 1;
        (a[i], a[from_end]) = (a[from_end], a[i]);
        i += 1;
    }
    a
}

fn main() {
    dbg!(K1, K2, K3);
}

Notes:

  • It's not possible to use for loops in const fns yet, because traits aren't supported and for uses Iterator which is a trait, so we have to use while for all our indexing.
  • array_reverse would not need T: Copy if std::mem::swap() were const, but it isn't yet.

The advantages of going to this effort over using Lazy, as suggested in another answer, are:

  • The constants can be used in situations where the numeric values are actually required to be constants — with Lazy accessing the value is only possible at run time.
  • Accessing the values doesn't require a run-time check for whether they've been initialized yet.
  • The type is an array type, rather than something that merely derefs to an array.
like image 152
Kevin Reid Avatar answered Jan 21 '26 08:01

Kevin Reid