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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With