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 `_`
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.