Creating two dimensional arrays in Rust
Asked Answered
N

12

76

How do I create an empty mutable two dimensional array in Rust?

This is what I have tried so far:

let mut state[[u8 * 4] * 4];

This produces the error

error: expected one of `:`, `;`, `=`, or `@`, found `[`
 --> src/main.rs:2:18
  |
2 |     let mut state[[u8 * 4] * 4];
  |                  ^ expected one of `:`, `;`, `=`, or `@` here
Nikolai answered 3/11, 2012 at 18:31 Comment(0)
H
32

Editor's note: This answer predates Rust 1.0 and some of the concepts and syntax have changed. Other answers apply to Rust 1.0.

Do you want the contents of the array to be mutable or the variable that holds it? If you want mutable contents, does this work for you?

let state = [mut [mut 0u8, ..4], ..4];

If you want the variable to be mutable but not the contents, try this:

let mut state = [[0u8, ..4], ..4];

Does this help? I didn't actually compile this, so the syntax might be slightly off.

Highkeyed answered 5/11, 2012 at 15:39 Comment(6)
Yes. This works. Do you know how would I pass such array to the function? The function needs to change values of array. Thanks.Nikolai
You could pass the value in two ways. One option would be &mut [[u8 * 4] * 4]--- pointer to a two-dimensional fixed length array. You would just do &mut state to get such a pointer.Matney
Oh, and normally having a mutable variable like this would also allow you to mutate the elements of a fixed-length array, as they are owned by the variable. Unfortunately this bug <github.com/mozilla/rust/issues/3226> will prevent you for now.Matney
@NikoMatsakis Thanks. I got it working like this & [mut [mut u8 * 4] * 4] for function parameter. How can I get pointer to first row of two dimensional array?Nikolai
For future visitors: this is for a pre-1.0 version of rust, there's another answer below with an updated syntax. let mut state = [[0u8; 4]; 4];.Morehouse
This does not work anymore, please consider removing this outdated answerKidding
E
95

In Rust 1.0, the following works:

let mut state = [[0u8; 4]; 6];
state[0][1] = 42;

Note that the length of the interior segment is an integral part of the type. For example, you can reference (and pass) state as follows:

let a: &[[u8; 4]] = &state;

but not without specifying the fixed length of the sub-array. If you need variable length sub-arrays you may need to do something like this:

let x: [Box<[u8]>; 3] = [
    Box::new([1, 2, 3]),
    Box::new([4]), 
    Box::new([5, 6])
];
let y: &[Box<[u8]>] = &x;
Ensnare answered 16/1, 2015 at 13:2 Comment(3)
Note that this panics if you access out of bounds. Which doesn't feel very rust-like.Panicstricken
Thanks hardy, but "(and, yes, the mutability works now)" -> how would you access and mutate one element ?Kidding
@AndyHayden in the two years since your comment was posted bounds checking must have been properly implemented, as I receive compile time errors when attempting out-of-bounds access on both the outer and inner arrays.Gage
H
32

Editor's note: This answer predates Rust 1.0 and some of the concepts and syntax have changed. Other answers apply to Rust 1.0.

Do you want the contents of the array to be mutable or the variable that holds it? If you want mutable contents, does this work for you?

let state = [mut [mut 0u8, ..4], ..4];

If you want the variable to be mutable but not the contents, try this:

let mut state = [[0u8, ..4], ..4];

Does this help? I didn't actually compile this, so the syntax might be slightly off.

Highkeyed answered 5/11, 2012 at 15:39 Comment(6)
Yes. This works. Do you know how would I pass such array to the function? The function needs to change values of array. Thanks.Nikolai
You could pass the value in two ways. One option would be &mut [[u8 * 4] * 4]--- pointer to a two-dimensional fixed length array. You would just do &mut state to get such a pointer.Matney
Oh, and normally having a mutable variable like this would also allow you to mutate the elements of a fixed-length array, as they are owned by the variable. Unfortunately this bug <github.com/mozilla/rust/issues/3226> will prevent you for now.Matney
@NikoMatsakis Thanks. I got it working like this & [mut [mut u8 * 4] * 4] for function parameter. How can I get pointer to first row of two dimensional array?Nikolai
For future visitors: this is for a pre-1.0 version of rust, there's another answer below with an updated syntax. let mut state = [[0u8; 4]; 4];.Morehouse
This does not work anymore, please consider removing this outdated answerKidding
C
31

You can create a dynamically-sized 2D vector like this:

fn example(width: usize, height: usize) {
    // Base 1d array
    let mut grid_raw = vec![0; width * height];

    // Vector of 'width' elements slices
    let mut grid_base: Vec<_> = grid_raw.as_mut_slice().chunks_mut(width).collect();

    // Final 2d array `&mut [&mut [_]]`
    let grid = grid_base.as_mut_slice();

    // Accessing data
    grid[0][0] = 4;
}
Candracandy answered 2/4, 2016 at 17:30 Comment(2)
I think it should read ...chunks_mut(height)... assuming access is grid[0...width-1][0...height-1]Sulphurate
Arrays and vectors are very different things.Mennonite
H
23

You can also create a 2D array like this (using Vec) if you don't have a known size at compile time:

let width = 4;
let height = 4;

let mut array = vec![vec![0; width]; height];

Use it like this:

array[2][2] = 5;

println!("{:?}", array);

Output:

0 0 0 0
0 0 0 0
0 0 5 0
0 0 0 0

Available since rust 1.0.0 https://doc.rust-lang.org/std/vec/struct.Vec.html

Humic answered 26/11, 2019 at 3:0 Comment(5)
This method has a problem: instead of idiomatic C arrays this code creates an array (Vec) of pointers (other Vec's) to different locations in the heap. It can cost you a significant performance loss on some array operations.Spar
I disagree with totikom. I'm not a rust expert, but I'm fairly sure that Vec contains its contents directly, not via a pointer (unless you make a Vec<Box<T>>). So a Vec<Vec<T>> has some overhead (the size of the header of Vec), but for grid size NxM it only has N overhead, not NxM (and much better cache locality).Concede
@Concede from the aforementioned link to the official doc: "Vec is and always will be a (pointer, capacity, length) triplet." it has N overhead, but each row can still end in a completely different location on the heap.Exarate
Thanks much! How to make it a vec of u64s?Arable
@Arable You can specify the type like this let mut array: Vec<Vec<u64>> = ...Humic
B
14

Initialization:
There are several approaches for 2D Array initialization:

  1. Using constants for M (rows) & N (columns)

    const M: usize = 2;
    const N: usize = 4;
    
    let mut grid = [[0 as u8; N] ; M];
    
  2. Explicit declaration with type annotations

    let mut grid: [[u8; 4]; 2] = [[0; 4]; 2];
    

Traversing:
The read-only traversing is as easy as:

for (i, row) in grid.iter().enumerate() {
    for (j, col) in row.iter().enumerate() {
        print!("{}", col);
    }
    println!()
}

or

for el in grid.iter().flat_map(|r| r.iter()) {
    println!("{}", el);
}

Updating element(s):

for (i, row) in grid.iter_mut().enumerate() {
    for (j, col) in row.iter_mut().enumerate() {
        col = 1;
    }
}
Baxie answered 26/12, 2018 at 10:17 Comment(0)
R
10

With explicit initialization

let directions: [[i32; 2]; 4] = [[-1, 0], [0, 1], [0, 1], [1, 0]]

With the same value

let directions: [[i32; 2]; 4] = [[0; 2]; 4];
Roux answered 8/2, 2021 at 0:50 Comment(0)
Q
8

If you are open to installing a crate, the ndarray can gracefully do it for you.

use ndarray::Array2;
let mut array = Array2::zeros((4, 3));
array[[1, 1]] = 7;

With some of the already existing answers, it is not possible to create an array using non-constant dimensions. There is no such problems with ndarray. You can also effortlessly create dimensions of more than two dimensions.

You can find more details here and here.

Quarters answered 19/4, 2020 at 19:32 Comment(0)
J
6

Well, the question of how to create a vector is properly addressed above. Here is the code snippet to create a two-dimensional vector and then fill it with user input:

use std::io;

fn main(){
    let width = 4;
    let height = 4;

    let mut array = vec![vec![0; width]; height];

    for i in 0..4 {
        let mut xstr = String::from("");
        io::stdin().read_line(&mut xstr).ok().expect("read error");
        array[i] = xstr
            .split_whitespace()
            .map(|s| s.parse().expect("parse error"))
            .collect();
    }

    println!("{:?}", array)
}

Jeffery answered 29/6, 2020 at 10:59 Comment(0)
G
5

Idiomatic C 2-dimensional arrays are declared using the same order of array sizes as used when accessing the array:

// Declaration
int array_2d[8][16]; // An 8 by 16 2D array
...
// Access
array_2d[0][1] = 5;

In Rust, the declaration sizes are flipped; to create an 8 by 16 2-dimensional array, the syntax is:

// Declaration
let mut array_2d: [[i32; 16]; 8];
...
// Access (same as idiomatic C. types for added explicitness)
array_2d[0_usize][1_usize] = 5;
Gig answered 15/2, 2018 at 17:23 Comment(1)
Can you please give an example of how would I pass such a 2D array to a function?Candlefish
Q
4

Another example of a two-dimensional string array:

fn main() {
    let width = 2;
    let height = 3;

    let mut a: Vec<Vec<String>> = vec![vec![String::from(""); width]; height];

    for i in 0..height {
        for j in 0..width {
            let s = format!("{}:{}", i + 1, j + 1);
            a[i][j] = s;
        }
    }
    println!("{:?}", a);
}

Output:

[
  ["1:1", "1:2"],
  ["2:1", "2:2"],
  ["3:1", "3:2"]
]

But I am not sure why we cannot use this form: a = [[String::from(""), 2], 3]. I don't understand the difference between vec![] and [].

Quantitative answered 11/10, 2020 at 14:3 Comment(1)
The last part looks like a follow-up question.Tanaka
B
2

You can use the vec! macro and specify the dimensions of the array like so:

let mut state = vec![[0; 4]; 4];

This creates a 4x4 array of u8 values with all elements initialized to 0.

Or you can use the arrayvec crate to create a fixed-size array like so:

use arrayvec::ArrayVec;

let mut state: ArrayVec<[[u8; 4]; 4]> = ArrayVec::new();

This creates an empty mutable 4x4 array of u8 values that can be resized up to a maximum of 4 elements.

Keep in mind that when creating a two-dimensional array in Rust, the innermost dimension should come first in the type, followed by the outer dimensions. So in the examples above, the type of state is Vec<[u8; 4]> or ArrayVec<[[u8; 4]; 4]>.

Belkisbelknap answered 22/12, 2022 at 0:19 Comment(0)
H
1

with macro vec![] ..

let n = 18;
let mut grid = vec![vec![0; n]; n];

grid[0][0] = 1;
Histrionics answered 16/1, 2023 at 0:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.