Typing on array size

I have an algorithm which utilises arrays of u32. It would be good to parameterise this size. Since the algorithm works with any size of array as long as the sizes in one operation are all identical.

So for the function

fn do_something(src: [u32;5], dst: [u32;5], weight: [u32;5]) 

it would be good to reuse the code as

fn do_something(src: [u32;7], dst: [u32;7], weight: [u32;7]) 

and ensure that all the sizes are the same.

I understand that this would need to be a compile time choice rather than a runtime choice.

Is there a way to do this? A link to the docs would be good, examples even better.

thanks in advance

Phill

i think const generics should work for that. Generic parameters - The Rust Reference

You would just add one const generic parameter and use that as the size for each array.

3 Likes

Thank you - that is almost exactly what I need.

Can such a type be applied to a struct or similar? So I could create an instance that used N=3 and enforce N=3 on the function call?

Phill

Yes, you can provide a const generic in the generic parameter list of the struct or similar and then use it for associated functions/methods and the types of fields.

https://play.rust-lang.org

4 Likes

Brilliant, thanks.

I am learning Rust in my normal fashion - by writing code. I find this easier than learning the manual - but it does mean I have to ask random, poorly phrased questions. So my gratitude for the help.

Coming from a long-time OO perspective I have found Rust both confusing and refreshing. My algorithm certainly runs faster now!

2 Likes

Yeah, I learned this way too: unfortunately this can lead you to run directly face-first into all the pointy bits of the language over and over!

The one thing I wish I got told earlier is that if I'm having trouble getting something working, either it taking more code than I think it should or something like continually running into a borrow check error no matter how I rearrange things, that I should probably ask here if there's a better option.

Since you're already here, the best I've got is don't forget about crates (lib.rs is a great way to find them), use clippy, and don't try to put a lifetime parameter in your state.

2 Likes

Thanks, that worked. However, I suspect there may be a problem lurking here that I will need to solve.

While preparing the data to create an instance of the parameterised class I need to call a function that relies on the value of LEN. Something like this:

struct Foo<const LEN: usize> {
    arr: [i32; LEN]
}

impl<const LEN: usize> Foo<LEN> {
    pub fn new() -> Self {
        let arr: [i32; LEN] = create_array([0; LEN])
        Self {
            arr
        }
    }
    
    pub fn replace(&mut self, arr: [i32; LEN]) {
        self.arr = arr;
    }
    
    fn create_array(src: [i32; LEN]) -> [i32; LEN] {
        (0 .. LEN).map(| i | {
            i * 2
        })
    }
}


fn main() {
    let mut a = Foo::new();
    // a.replace([1, 2, 3]); // compilation err: expected an array with a size of 2, found one with a size of 3
    // let b = Foo::<1>::new([1,2]); // compilation err: expected an array with a size of 1, found one with a size of 2
}

How does the create_array function get the correct value of LEN if I create both a Foo<5> and a Foo<3>?

struct Foo<const LEN: usize> {
    arr: [i32; LEN],
}

impl<const LEN: usize> Foo<LEN> {
    pub fn new() -> Self {
        let arr: [i32; LEN] = Self::create_array();
        Self { arr }
    }

    pub fn replace(&mut self, arr: [i32; LEN]) {
        self.arr = arr;
    }

    fn create_array() -> [i32; LEN] {
        const { assert!(LEN < i32::MAX as u32 as usize) };
        std::array::from_fn(|i| i32::try_from(i).unwrap() * 2)
    }
}
1 Like

And if the function is defined outside of the impl block of Foo you need to add the same const generic as for Foo:

fn create_array<const LEN: usize>() -> [i32; LEN] {
    const { assert!(LEN <= i32::MAX as usize, "LEN is too large to fit in i32") };
    std::array::from_fn(|i| i32::try_from(i).unwrap() * 2)
}
2 Likes

They produce the same assembly, with or without the cast; and I avoid as as much as possible.

2 Likes

True, I have edited my answer

1 Like

@jendrikw @A1cey Thank you both for that - I can’t say that I fully understand but I will try it! Once I get more familiar with the language I am sure it will ‘click’.

And yes, I have way too many ‘as’ around my code. But it seems I need to keep jumping between types. I will go back and sort them out once I find a good strategy.

2 Likes