Home > other >  Create 2D array from 1D array in Rust
Create 2D array from 1D array in Rust

Time:07-13

I have two 2D arrays in Rust like this

    let d1: [[T; 3]; 3] = ...
    let d2: [[T; 3]; 3] = ...

Now I want to add the elements element-wise and assign the result to a new 2D array.

My current solution looks like this:


    let mut result = [[T::default(); ROW]; COL];
    
    for (i, row) in d1.iter().enumerate() {
        for (j, element) in row.iter().enumerate() {
            result[i][j] = *element   d2[i][j];
        }
    }

This is inefficient as it first fills result with default values only to override them later.

Therefore I want to solve this using iterators by flattening the 2D arrays and then adding the elements.

However, I don't know how I can construct a 2D array from the resulting flattened 1D iterator :

    let result = for sum in d1.iter().flatten().zip(
                            d2.iter().flatten())
                            .map(|(x, y)| x y)
                            .collect().???

CodePudding user response:

This code adds a 2D array without initializing it with default elements, but it does go through a Vec to array conversion, because you cannot collect directly into an array. I estimate the likelihood of this being optimized away to be much lower than for your default elements solution.

fn add_3by3(a: [[u32; 3]; 3], b: [[u32; 3]; 3]) -> [[u32; 3]; 3] {
    a.iter()
        .zip(b.iter())
        .map(|(ai, bi)| {
            ai.iter()
                .zip(bi.iter())
                .map(|(aij, bij)| aij   bij)
                .collect::<Vec<_>>()
                .try_into()
                .unwrap()
        })
        .collect::<Vec<_>>()
        .try_into()
        .unwrap()
}

Incidentally, I think it is probably a better idea to keep your data in a 1D array no matter what actual shape it has, and keep shape information on the side. That way every element-wise operation is very simple.

CodePudding user response:

You can do a hybrid approach.

You can initialize a fixed-size array via first constructing a [(); N] and then calling .map() on it. Combined with iterators over the input arrays, this should be somewhat performant.

Although I still recommend benchmarking it vs the naive solution.

fn add_2d<'a, 'b, T, const W: usize, const H: usize>(
    d1: &'a [[T; W]; H],
    d2: &'b [[T; W]; H],
) -> [[T; W]; H]
where
    T: 'static,
    &'a T: std::ops::Add<&'b T, Output = T>,
{
    let mut iter_row = d1.iter().zip(d2.iter());

    [(); H].map(move |()| {
        let (row1, row2) = iter_row.next().unwrap();
        let mut elem_iter = row1.iter().zip(row2.iter());
        [(); W].map(move |()| {
            let (elem1, elem2) = elem_iter.next().unwrap();
            elem1   elem2
        })
    })
}

fn main() {
    let d1: [[u32; 2]; 3] = [[1, 2], [4, 5], [7, 8]];
    let d2: [[u32; 2]; 3] = [[10, 20], [40, 50], [70, 80]];

    let sums = add_2d(&d1, &d2);
    println!("{:?}", sums);
}
[[11, 22], [44, 55], [77, 88]]

Alternatively, if you don't mind unsafe, this is what I would probably go for to achieve maximum performance:

use std::mem::MaybeUninit;

fn add_2d<'a, 'b, T, const W: usize, const H: usize>(
    d1: &'a [[T; W]; H],
    d2: &'b [[T; W]; H],
) -> [[T; W]; H]
where
    T: 'static,
    &'a T: std::ops::Add<&'b T, Output = T>,
{
    let mut result: MaybeUninit<[[T; W]; H]> = MaybeUninit::uninit();

    let arr_ptr = result.as_mut_ptr() as *mut [T; W];

    unsafe {
        for y in 0..H {
            let row_ptr = arr_ptr.add(y) as *mut T;
            for x in 0..W {
                row_ptr.add(x).write(&d1[y][x]   &d2[y][x]);
            }
        }

        result.assume_init()
    }
}

fn main() {
    let d1: [[u32; 2]; 3] = [[1, 2], [4, 5], [7, 8]];
    let d2: [[u32; 2]; 3] = [[10, 20], [40, 50], [70, 80]];

    let sums = add_2d(&d1, &d2);
    println!("{:?}", sums);
}
[[11, 22], [44, 55], [77, 88]]

Be sure to use .write() instead of a simple assignment operation, because the assignment operation would run the Drop function of the previous, uninitialized element. Which would cause undefined behaviour.

  • Related