Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"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 `_`
like image 383
dave Avatar asked Nov 04 '16 10:11

dave


1 Answers

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.

like image 79
Djzin Avatar answered Nov 02 '22 15:11

Djzin