Quickz
Quickz

Reputation: 1836

What is the correct syntax for creating a dynamic 2D array in Rust?

My intention is to create a dynamic 2D array with multiple rows and columns. How can I achieve that?

Attempted solution

The following line of code works but the array ends up having only a single row.

let matrix: &mut [&mut [i32]] = &mut [&mut [0; 3]];

In order to create an array with 3 rows instead of one I tried the following piece of code but it produced a compile time error.

let matrix: &mut [&mut [i32]] = &mut [&mut [0; 3]; 3];
mismatched types

expected slice `[&mut [i32]]`, found array `[&mut [{integer}; 3]; 3]`

note: expected mutable reference `&mut [&mut [i32]]`
         found mutable reference `&mut [&mut [{integer}; 3]; 3]`rustc(E0308)
main.rs(7, 17): expected due to this
main.rs(7, 37): expected slice `[&mut [i32]]`, found array `[&mut [{integer}; 3]; 3]`

Upvotes: 3

Views: 2804

Answers (1)

Adam
Adam

Reputation: 905

Based on your question it looks like you're trying to use a matrix object. There are multiple ways to do that in rust:

Compile-Time matrices: Using const generics it's now very easy to define the matrix object with an array of arrays:

pub struct Matrix<T, const ROWS: usize, const COLS: usize> {
    data: [[T; COLS]; ROWS],
}

impl<T, const ROWS: usize, const COLS: usize> Matrix<T, ROWS, COLS> {
    pub fn new(data: [[T; COLS]; ROWS]) -> Self {
        Self { data }
    }
}

impl<T, const ROWS: usize, const COLS: usize> Index<(usize, usize)> for Matrix<T, ROWS, COLS> {
    type Output = T;

    fn index(&self, index: (usize, usize)) -> &Self::Output {
        &self.data[index.0][index.1]
    }
}

Here, the amount of rows and columns are hard-coded into the data-type. So to resize you need to a create a new object and all types must be known (defined) at compile time.

Dynamic matrices:

If you want run-time (dynamic) sizing, the simplest solution is to use a vector of vectors:

pub struct Matrix<T> {
    data: Vec<Vec<T>>,
}

impl<T> Matrix<T> {
    pub fn new(data: Vec<Vec<T>>) -> Self {
        Self { data }
    }
}

impl<T> Index<(usize, usize)> for Matrix<T> {
    type Output = T;

    fn index(&self, index: (usize, usize)) -> &Self::Output {
        &self.data[index.0][index.1]
    }
}

Note that since vectors are pointers to memory on the heap, the items won't usually be contiguous in memory.

You can build contiguous dynamic matrices by using a single vector and indices to map to chunks of it:

pub struct Matrix<T> {
    rows: usize,
    cols: usize,
    data: Vec<T>,
}

impl<T> Matrix<T> {
    pub fn new(rows: usize, cols: usize, data: Vec<T>) -> Self {
        assert_eq!(rows * cols, data.len());
        Self { rows, cols, data }
    }
}

impl<T> Index<(usize, usize)> for Matrix<T> {
    type Output = T;

    fn index(&self, index: (usize, usize)) -> &Self::Output {
        &self.data[index.0 * self.cols + index.1]
    }
}

Upvotes: 4

Related Questions