I have a strange behavior that I cannot explain to myself. If want to create new Strings by iterating over two Vec<String>
's, but somehow access by index doesn't work, because I borrow stuff. My confusion comes from the fact that the index should not be copied as it is a primitive time.
The example does work if I iterate directly over the Vec<String>
's. The compiler error's suggestion of using move
in the second map
does not fix the problem.
Here is my example:
fn f() -> Vec<String> {
let num_outer = 2;
let num_inner = 3;
let outer_names = vec!["1", "2"];
let inner_names = vec!["x", "y", "z"];
(0..num_outer)
.into_iter()
.map(|outer_index| {
(0..num_inner).into_iter().map(|inner_index| {
format!(
"tuple ({},{})",
outer_names[outer_index], inner_names[inner_index],
)
})
})
.flatten()
.collect::<Vec<String>>()
}
fn main() {
let x = f();
println!("{:?}", &x);
}
This is because you need your closures to capture the indices by move
(or by Copy
, since they're Copy
types), but also capture the Vec
s by reference. Rust only lets you choose one type of capture syntactically (it's by move
if you make a move
closure, otherwise it captures everything by reference).
However, it's easy to do it manually. You just use a move
closure and "move-in" references that you create before the closure. In your case, this works:
fn f() -> Vec<String> {
let num_outer = 2;
let num_inner = 3;
let outer_names = vec!["1", "2"];
let inner_names = vec!["x", "y", "z"];
let outer_names_ref = &outer_names;
let inner_names_ref = &inner_names;
(0..num_outer)
.flat_map(|outer_index| {
(0..num_inner).map(move |inner_index| {
format!(
"tuple ({},{})",
outer_names_ref[outer_index], inner_names_ref[inner_index],
)
})
})
.collect::<Vec<String>>()
}
fn main() {
let x = f();
println!("{:?}", &x);
}
Note I also removed the unnecessary into_iter
s, since the range literals are already iterators. I also switched to flat_map
because clippy suggested it (use clippy! it's great :) )
In general, if you need "mixed captures" for a closure (i.e., move captures for some variables, reference captures for others), this is the way to do it. You use a move
closure, but you explicitly create the references that you need to move in to capture things "by reference".