A question regarding the "vec!" macro


#1

The literal for a 2D Vec is not nice because you have to repeat “vec!” for each row:

let m1 = vec![vec![1, 2, 3],
              vec![4, 5, 6],
              vec![7, 8, 9]];

And things get worse for 3D Vecs (thankfully such literals are not common):

let t1 = vec![vec![vec![1, 2], vec![3, 4]],
              vec![vec![1, 2], vec![3, 4]],
              vec![vec![1, 2], vec![3, 4]]];

Has someone suggested to introduce a macro like “tensor!” that applies “vec!” recursively? It should allow to write code like:

let m2 = tensor![[1, 2, 3],
                 [1, 2, 3],
                 [1, 2, 3]];

let t2 = tensor![[[1, 2], [3, 4]],
                 [[1, 2], [3, 4]],
                 [[1, 2], [3, 4]]];

Is it worth having?

Note: if you don’t need Vecs all the way down (like here where the innermost array is a [i32; 2]) then you have to use “vec!” again, you can’t use “tensor!”:

let t3 = vec![vec![[1, 2], [3, 4]],
              vec![[1, 2], [3, 4]],
              vec![[1, 2], [3, 4]]];

#2

(On the playpen)

macro_rules! tensor {
    (@accum_subs [$(($($subs:tt)*))*] $(,)*) => {
        vec![$(tensor![$($subs)*]),*]
    };
    
    (@accum_subs [$($subs:tt)*], [$($sub:tt)*] $($tail:tt)*) => {
        tensor![@accum_subs [$($subs)* ($($sub)*)] $($tail)*]
    };
    
    ([$($sub:tt)*] $($tail:tt)*) => {
        tensor![@accum_subs [($($sub)*)] $($tail)*]
    };
    
    ($($elems:expr),* $(,)*) => {
        vec![$($elems),*]
    };
}

fn main() {
    let a: Vec<i32> = tensor![1, 2, 3];
    let b: Vec<Vec<i32>> = tensor![[1, 2, 3], [4, 5, 6]];
    let c: Vec<Vec<Vec<i32>>> = tensor![[[1], [2]], [[3], [4]]];
    
    println!("a: {:?}", a);
    println!("b: {:?}", b);
    println!("c: {:?}", c);
}

So it’s kinda ugly, and has O(n)-ish expansion complexity. Not great.


#3

The problem with construction high-dimensional arrays like that is that you get lots of indirection. You end of with a vec of references to vecs of references. Would be much better to have matrices and tensors as just a plain vec wrapped in a struct that translates cartesian coordinates into linear ones.


#4

I suspect the question is how often are people creating multi-dimension vectors? And how big are those structures? I don’t think someone would write out a 25x25x25 declaration in the code but rather build that structure dynamically at runtime.

Personally I use vec! very little, often preferring to use just [] to declare an array of a known size, or Box<[]> if the array is too big for the stack. I guess I could see someone writing a 3D engine or vector math based library needing to create lots of multi dimension structures and what not, but for the bulk of applications not so much.

If all your arrays are the same size and don’t need to grow you can do:

let t1 = [
    [[1, 2], [3, 4]],
    [[1, 2], [3, 4]],
    [[1, 2], [3, 4]]
];

Which is basically your tensor, just without the macro.


#5

Thanks for all the answers and the code. I have now written some Rust code, but not enough to know how often “tensor!” could be useful.