Within a struct, how can I reference another value in it?

I have a struct Matrix and within it, I want it so if you create a new array and you're able to set the rows and columns for the array, like this:

struct Matrix {
    augmented: bool,
    columns: i32,
    rows: i32,
    array: [[f32;columns];rows],
}

When I run it, it says not found in this scope for the columns and rows within the array part. I've tried to use other methods rather than just directly referencing it but none of them seem to work.

Is there any way I can do this or somehow have the array be a dynamic size?

I'm very new to rust, so this is probably just a misunderstanding from my end about the syntax.

Unfortunately you can neither reference fields inside other fields in your struct definition, nor are arrays without a constant size possible in rust.
You can use the dynamically sized Vec type instead of an array like this, for example:

struct Matrix {
    augmented: bool,
    columns: i32,
    rows: i32,
    array: Vec<Vec<f32>>,
}

impl Matrix {
    fn new(augmented: bool, columns: i32, rows: i32) -> Matrix {
        Matrix {
            augmented,
            columns,
            rows,
            array: vec![vec![0.; columns as usize]; rows as usize],
        }
    }
}

Playground.

1 Like

In addition to the Vec solution proposed by @jofas, if you don't actually need to be able to resize the array at runtime (which would be the case if you are trying to implement mathematical matrices, as you appear to be), you can use const generics to have a more flexible approach while still having arrays with size determined at compile time:

#[derive(Debug)]
struct Matrix<const COLS: usize, const ROWS: usize> {
    augmented: bool,
    array: [[f32; COLS]; ROWS],
}

impl<const M: usize, const N: usize> Matrix<M, N> {
    fn new() -> Self {
        Matrix::<M, N> {
            augmented: false,
            array: [[0.0; M]; N],
        }
    }
}

fn main() {
    let mat = Matrix::<2, 3>::new();
    println!("{:?}", mat);
}

(playground)

3 Likes

Oh that's interesting. Why should I use the ::new() if you can just use the struct directly?

Privacy. If you want to be able to construct the struct field-by-field, you have to expose all of its fields as public. This may be fine if the struct doesn't have any internal invariant to be upheld, but yours does. (array.len() == rows and array[i].len() == columns for all i)

It's also good practice to provide a simple "constructor" that is appropriate to call in the majority of the use cases of the type, without always having to think about all of its field names.

3 Likes

I'm mainly presenting an example of a constructor function to build a default blank array there. It's useful to have one, but you can also define other functions, and if you expose Matrix as part of a library that may be used by others, then you may not want them to be able to change all parts of the struct (for example, augmented may only be set by a specific operation).

1 Like