I am trying to make a 2-dimensional matrix from a functor that creates each element, and store it as a flat Vec
(each row concatenated).
I used nested map
(actually a flat_map
and a nested map
) to create each row and concatenate it. Here is what I tried:
fn make<T, F>(n: usize, m: usize, f: F) -> Vec<T>
where
F: Fn(usize, usize) -> T,
{
(0..m).flat_map(|y| (0..n).map(|x| f(x, y))).collect()
}
fn main() {
let v = make(5, 5, |x, y| x + y);
println!("{:?}", v);
}
Unfortunately, I get an error during compilation:
error[E0597]: `y` does not live long enough
--> src/main.rs:5:45
|
5 | (0..m).flat_map(|y| (0..n).map(|x| f(x, y))).collect()
| --- ^ - - borrowed value needs to live until here
| | | |
| | | borrowed value only lives until here
| | borrowed value does not live long enough
| capture occurs here
How does one use closures in nested maps? I worked around this issue by using a single map on 0..n*m
, but I'm still interested in the answer.
In your case the inner closure |x| f(x,y)
is a borrowing closure, which takes its environment (y
and f
) by reference.
The way .flat_map(..)
works, it forbids you to keep a reference to y
, which is not from the outer scope. Thus we need to have your closure take its environment by value, which is not a problem for y
being a usize
which is Copy
:
(0..m).flat_map(|y| (0..n).map(move |x| f(x, y))).collect()
However, now another problem arises:
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> src/main.rs:5:36
|
1 | fn make<T, F>(n: usize, m: usize, f: F) -> Vec<T>
| - captured outer variable
...
5 | (0..m).flat_map(|y| (0..n).map(move |x| f(x,y))).collect()
| ^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure
Here, we are trying to move f
as well into the closure, which is definitely not possible (unless m
is 1
, but the compiler cannot know that).
Since f
is a Fn(usize, usize) -> T
, we could just as well explicitly pass a &
reference to it, and &
references are Copy
:
fn make<T, F>(n: usize, m: usize, f: F) -> Vec<T>
where
F: Fn(usize, usize) -> T,
{
let f_ref = &f;
(0..m)
.flat_map(|y| (0..n).map(move |x| f_ref(x, y)))
.collect()
}
In this case, the closure takes its environment by value, and this environment is composed of y
and f_ref
, both of them being Copy
, everything is well.
Adding to Levans's excellent answer, another way of defining the function would be
fn make<T, F>(n: usize, m: usize, f: F) -> Vec<T>
where
F: Fn(usize, usize) -> T + Copy,
{
(0..m).flat_map(|y| (0..n).map(move |x| f(x, y))).collect()
}
Since we know that |x, y| x + y
is a Copy
type, f
would get copied for every callback that flat_map
invokes. I would still prefer Levans's way as this would not be as efficient as copying a reference.
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