Search code examples
arraysrustinference

"cannot infer type for `_`" when using map on iter in Rust


I'm having an issue where I'm trying to initialise a 2D array of booleans with random true/false values but the compiler doesn't seem to be able to infer the types I need; I am just wondering what I need to specify for the inference engine to be able to resolve this.

extern crate rand;

fn main() {
    let mut grid = [[false; 10]; 10];
    grid.iter_mut().map(|row| { [false; 10].iter().map(|_| { rand::random() }).collect() });
}

Playground link (without rand::random())

The error I'm getting is

   | grid.iter_mut().map(|row| { [false; 10].iter().map(|_| { rand::random() }).collect() });
   |                 ^^^ cannot infer type for `_`

Solution

  • Since the type [T; 10] implements Rand where T: Rand, you can use rand::random() directly:

    extern crate rand;
    
    fn main() {
        let grid: [[bool; 10]; 10] = rand::random();
        println!("{:#?}", grid);
    }
    

    As for why type inference is failing in your example - here's something slightly simpler that illustrates the problem:

    fn main() {
        let mut arr = [false; 10];
        let mapped = arr.iter_mut().map(|_| rand::random()).collect();
        println!("{:?}", arr);
        println!("{:?}", mapped);
    }
    

    Gives the error:

    error[E0282]: unable to infer enough type information about `_`
     --> src/main.rs:5:13
      |
    5 |         let mapped = arr.iter_mut().map(|_| rand::random()).collect();
      |             ^^^^^^ cannot infer type for `_`
      |
      = note: type annotations or generic parameter binding required
    

    So we can specify the type:

    fn main() {
        let mut arr = [false; 10];
        let mapped = arr.iter_mut().map(|_| rand::random()).collect::<[bool; 10]>();
        println!("{:?}", arr);
        println!("{:?}", mapped);
    }
    

    Note the use of the "turbofish" operator ::<> after collect to specify the type to collect into, in this case ::<[bool; 10]>. Unfortunately here the compiler will complain:

    error[E0277]: the trait bound `[_; 10]: std::iter::FromIterator<bool>` is not satisfied
    

    So what is std::iter::FromIterator<bool>? Well, consider the collect function's definition:

    fn collect<B>(self) -> B
        where B: FromIterator<Self::Item>
    

    This means whatever type you are collecting into needs to implement FromIterator<Self::Item>. Arrays do not, unfortunately, implement FromIterator - but there are many possible types that do, for instance Vec, VecDeque, HashSet, BTreeSet and so on. So we can modify the example:

    fn main() {
        let mut arr = [false; 10];
        let mapped = arr.iter_mut().map(|_| rand::random()).collect::<Vec<bool>>();
        println!("{:?}", arr);
        println!("{:?}", mapped);
    }
    

    However, this might not give you the result you were hoping for:

    [false, false, false, false, false, false, false, false, false, false]
    [true, false, false, true, true, false, true, false, true, true]
    

    So what gives? Why wasn't the arr mutated even though it was declared mutable, and we used iter_mut? The reason is that map produces a new object from the existing one - it doesn't map "in-place". If you really wanted to map in-place, you could use the following:

    fn main() {
        let mut arr = [false; 10];
        let mapped = arr.iter_mut().map(|b| *b = rand::random()).collect::<Vec<()>>();
        println!("{:?}", arr);
        println!("{:?}", mapped);
    }
    

    Yielding

    [true, false, true, true, true, false, false, false, true, true]
    [(), (), (), (), (), (), (), (), (), ()]
    

    However, this use of iterators is considered unidiomatic (not to mention confusing) - the idiomatic way would be to use a for loop:

    fn main() {
        let mut arr = [false; 10];
        for b in &mut arr {
            *b = rand::random();
        }
        println!("{:?}", arr);
    }
    
    [false, true, true, true, false, false, true, false, true, false]
    

    Much better. Of course in this particular case, my first example is probably the way to go.