Search code examples
rustcompiler-errorsfunctional-programmingiteratortraits

Convert for loop to iterator in rust


So, I am trying to build a vector of vectors of a custom type.I am trying to implement a default for it and was successful with a for loop.

let mut data: Vec<Vec<Cell>> = Vec::new();
    for _i in 0..63 {
        let mut row: Vec<Cell> = Vec::with_capacity(64);
        for _j in 0..63 {
            row.push(Cell::default())
        }
        data.push(row);
   }

I felt this code could do with some functional style and interators, so I decided to do something like so:

let data: Vec<Vec<Cell>> = Vec::with_capacity(64)
    .iter_mut()
    .map(|mut x: &mut Vec<Cell>| {
        x = Vec::with_capacity(64)
            .iter_mut()
            .map(|mut y: Cell| y = Cell::default())
            .collect()
        })
    .collect();

With this, I get an error like so:

error[E0631]: type mismatch in closure arguments
   --> src/types.rs:124:26
    |
124 |                         .map(|mut y: Cell| y = Cell::default())
    |                          ^^^ ------------- found signature defined here
    |                          |
    |                          expected due to this
    |
    = note: expected closure signature `fn(&mut _) -> _`
               found closure signature `fn(types::cell::Cell) -> _`
note: required by a bound in `map`
   --> /home/naitik/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:779:12
    |
779 |         F: FnMut(Self::Item) -> B,
    |            ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`

I do not understand the problem here. What exactly is it? What can I do to fix it?


Solution

  • You're misunderstanding how Vec::with_capacity works. It doesn't actually put anything into a vec that you can iterate over and assign to. You want to map Cell::default over something and then collect that into a vec. That something doesn't matter, only that there's 64 of them, so a range will do:

    let data: Vec<Vec<Cell>> = (0..64)
            .map(|_| (0..64).map(|_| Cell::default()).collect())
            .collect();
    

    collect should hopefully figure out the needed capacities from the ranges' TrustedLen impl on its own to avoid unnecessary re-allocations.

    I would, however, doubt that this is better than the procedural approach. It seems less clear and harder to modify to me. Just stick to good ol' for loops is my two cents.

    If Cell is Clone or Copy you could even just do:

    let data: Vec<Vec<Cell>> = vec![vec![Cell::default(); 64]; 64];
    

    for maximal minimality.