Creating a Vector of Vectors in Rust

2020-03-23 18:26发布

问题:

This code won't compile:

fn main() {
    let m1 = vec![1, 2, 3];
    let m2 = vec![&m1, &m1, &m1];
    let m3 = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];

    for i in &m2 {
        for j in i {
            println!("{}", j);
        }
    }

    for i in &m3 {
        for j in i {
            println!("{}", j);
        }
    }
}
error[E0277]: the trait bound `&&std::vec::Vec<{integer}>: std::iter::Iterator` is not satisfied
 --> src/main.rs:8:18
  |
8 |         for j in i {
  |                  ^ `&&std::vec::Vec<{integer}>` is not an iterator; maybe try calling `.iter()` or a similar method
  |
  = help: the trait `std::iter::Iterator` is not implemented for `&&std::vec::Vec<{integer}>`
  = note: required by `std::iter::IntoIterator::into_iter`
  1. How is m2 different than m3 such that m3 causes no issues, but m2 prevents compilation?

  2. Is there an easier way to create a vector of vector of... to any desired depth? The way I have it working (m3) seems so clunky.

回答1:

How is m2 different than m3 ...

Check the types step by step. m1 is of type Vec<isize> (it could be any other integer type as well, but I assume it's isize for now). Why? Because the elements in the vec![] macro are of type isize. Now you are creating m2:

let m2 = vec![&m1, &m1, &m1];

What is the type of the elements in this macro? Well we already said m1 has the type Vec<isize>, so &m1 has the type &Vec<isize>. So the resulting type of m2 is Vec<&Vec<isize>> (a vector full of references to other vectors).

However, m3 is of type Vec<Vec<isize>>, since the elements in the (outer) vec![] macro are of type Vec<isize> (no reference!).

Hint: to easily check the type of any variable (such as foo), type:

let _: () = foo;

This will result in a compiler error that tells you the type of foo.

... such that m3 causes no issues, but m2 prevents compilation?

Now that we know the types of m2 and m3, lets look at the loops. for loops work by accepting something that implements IntoIterator. You are passing &m2, which is of type &Vec<&Vec<isize>> (note the two references). We can see, that IntoIterator is indeed implemented for a reference to a vector:

impl<T> IntoIterator for &Vec<T> {
    type Item = &T
    // ...
}

This means that you get an iterator that spits out references to the inner type T (type Item = &T). Our inner type of m2 is &Vec<isize>, so we will get items of type &&Vec<isize> (two references!). Your variable i has this exact type.

Then you want to iterate again with this inner loop:

for j in i { ... }

But i has this double-reference type and there isn't an implementation of IntoIterator for that type. To iterate it, you have to either dereference it like:

for j in *i { ... }

Or even better: make i be of the type &Vec<isize> (one reference!) by stripping it away with pattern matching in the outer loop:

for &i in &m2 { ... }

Your m3 loop does the same, but since m3 is of another type (with one reference less), it works (I hope you can see why).


Is there an easier way to create a vector of vector of... to any desired depth

Even if m2 worked, it wouldn't hold the same values as m3. To make m2 of the type Vec<Vec<isize>> (like m3), you should clone m1 instead of taking a reference to it.

let m2 = vec![m1.clone(), m1.clone(), m1.clone()];

We can do even better by using the vec![_; _] form of the macro:

let m2 = vec![m1; 3];   // three times the value of `m1`

As a last note, you should consider not using nested Vecs. The nesting creates overhead because the values are spread over the whole memory instead of being in one place.