Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutable multidimensional array as a function argument

Tags:

rust

In rustc 1.0.0, I'd like to write a function that mutates a two dimensional array supplied by the caller. I was hoping this would work:

fn foo(x: &mut [[u8]]) {
    x[0][0] = 42;
}

fn main() {
    let mut x: [[u8; 3]; 3] = [[0; 3]; 3];
    foo(&mut x);
}

It fails to compile:

$ rustc fail2d.rs
fail2d.rs:7:9: 7:15 error: mismatched types:
 expected `&mut [[u8]]`,
    found `&mut [[u8; 3]; 3]`
(expected slice,
    found array of 3 elements) [E0308]
fail2d.rs:7     foo(&mut x);
                    ^~~~~~
error: aborting due to previous error

I believe this is telling me I need to somehow feed the function a slice of slices, but I don't know how to construct this.

It "works" if I hard-code the nested array's length in the function signature. This isn't acceptable because I want the function to operate on multidimensional arrays of arbitrary dimension:

fn foo(x: &mut [[u8; 3]]) {  // FIXME: don't want to hard code length of nested array
    x[0][0] = 42;
}

fn main() {
    let mut x: [[u8; 3]; 3] = [[0; 3]; 3];
    foo(&mut x);
}

tldr; any zero-cost ways of passing a reference to a multidimensional array such that the function use statements like $x[1][2] = 3;$?

like image 477
romanows Avatar asked May 21 '15 06:05

romanows


1 Answers

This comes down to a matter of memory layout. Assuming a type T with a size known at compile time (this constraint can be written T: Sized), the size of [T; n] is known at compile time (it takes n times as much memory as T does); but [T] is an unsized type; its length is not known at compile time. Therefore it can only be used through some form of indirection, such as a reference (&[T]) or a box (Box<[T]>, though this is of limited practical value, with Vec<T> which allows you to add and remove items without needing to reallocate every single time by using overallocation).

A slice of an unsized type doesn’t make sense; it’s permitted for reasons that are not clear to me, but you can never actually have an instance of it. (Vec<T>, by comparison, requires T: Sized.)

&[T; n] can coerce to &[T], and &mut [T; n] to &mut [T], but this only applies at the outermost level; the contents of slice is fixed (you’d need to create a new array or vector to achieve such a transformation, because the memory layout of each item is different). The effect of this is that arrays work for single‐dimensional work, but for multi‐dimensional work they fall apart. Arrays are currently very much second‐class citizens in Rust, and will be until the language supports making slices generic over length, which it is likely to eventually.

I recommend that you use either a single‐dimensional array (suitable for square matrices, indexed by x * width + y or similar), or vectors (Vec<Vec<T>>). There may also be libraries already out there abstracting over a suitable solution.

like image 72
Chris Morgan Avatar answered Sep 30 '22 11:09

Chris Morgan