Create a mutable multi-dimensional array (or vector) of a Struct

Hi every one.
I am new to Rust (like today-new), and since the document for this language is relatively little, I couldn't find what I am looking for, at least not easily.
I have a struct called Node that have special characteristic (say "Color") that I want to be mutable.

struct Node {
    R: i32,
    G: i32,
    B: i32,
}

As it's a node, it should have some coordinate, which I will denote using a 2D array. Therefore, I want to create a 2D array (say maybe with width 200 and height 300) called B of this Node struct. So accessing and modifying a node could be done simply B[0][0].B=69 or something like that. (sorry, can't help it)

Since this might be a common question for future reference if the language blow up, I just want some good details:

  1. What is the shortest and most simple syntax to achieve this task.
  2. Assuming I do not want to create an array, but rather a vector for future expansion, what cloud be a simple syntax for this?
  3. I did some reading and it seems that creating a 2D vector will potentially consume computer resources a lot. Is that true? Does that mean, in general, I should use an array if I am pretty positive that my data size wouldn't change, or use 1D vector with a length equal to width*height instead? Basically what is the optimal solution to handle 2D data problem like I just mention.

EDIT:
Copy/Pasted the Solution from idkravitz for convenience.


    #![feature(new_uninit)] // required for "option 2"

    #[derive(Copy,Clone,Debug)] // Debug here is just for printing
    struct Node {
        r: i32,
        g: i32,
        b: i32,
    }

    fn main() {
    // option 1
        let arr = Box::new([[Node { r: 0, g: 0, b: 0}; 300]; 200]);
        println!("{:?}", arr[5][1]);
        
    // option 1' (with nightly and unsafe, won't cause SO on large sizes)
    // but it's generally not recommended especially for complex types
        let arr = Box::new_zeroed();
        let arr: Box<[[Node; 300]; 200]> = unsafe { arr.assume_init() };
        println!("{:?}", arr[5][1]);

    // option 2 with vec
       let arr = vec![[Node{r: 42, g: 42, b: 42}; 300]; 200];
       println!("{:?}", arr[5][1]);

    // option 3 with vec and linear indexing
       let (width, height) = (200, 300);
       let arr = vec![Node{r:10, g: 20, b: 30}; width*height];
       println!("{:?}", arr[5 * width + 1]); // corresponds to arr[5][1]
    }

AFAIK the problem with multidimensional vector (std::Vec) is that there is none, you can have vector of vectors, and have desired indexing syntax (v[0][0].B = 1), but at the cost of: extra indirection (access happens through 2 pointers), a bit worse caching behaviour (your rows may be allocated far from each other), x2 mem usage at worst (each vector may reserve x2 capacity), but you can call shrink on each or operate on them with reserved capacity, so this isn't much of issue.

Ways to solve it:

  1. Use fixed array, you may have a Box<[[Node;200];300]> definition for your storage array. Cons: size should be defined at compile time, Pros: all above problems solved. I recommend box it (hence Box<.. >) to ensure its heap allocated, but it's tricky to initialize it without causing stack overflow.
  2. use Vec<[Node; WIDTH]>, where WIDTH is constant, yet again, width is fixed at compile time, but height can change at runtime, this one operates using linear indexing, like fixed array and also its easier to initialize for big sizes, than Box<[..]> variant.
  3. Vec with linear indexing. You could have a 1d array of size width*height, and use the indexing formulla v[y*width + x]. Cons: the indexing is harder, though you can abstract it with helper functions or you can write a view object that would support double indexing. Pros: size can be adjusted at runtime
  4. Use 3rd party crate like ndarray, vector2d, etc., can't recommend one, since I haven't used any myself.

I tried the first solution, but it seems I have to initialize the array first. So how to initialize a 2D-array with custom type efficiently? Overall I just want a one-liner solution to initialize a 2D array with custom type I guess.

Here are examples for options 1-3

If you're okay with fixed sizes (width at least) then option 2 (with vec of arrays) is better than boxed fixed 2d array

1 Like