Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use closures in a nested map?

Tags:

closures

rust

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.

like image 545
Gyscos Avatar asked Jun 04 '15 23:06

Gyscos


2 Answers

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.

like image 137
Levans Avatar answered Oct 22 '22 07:10

Levans


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.

like image 31
vikram2784 Avatar answered Oct 22 '22 08:10

vikram2784