Salim Fadhley
Salim Fadhley

Reputation: 8175

Rust, make a closure inside a closure avoiding "closure may outlive the current function"

I'm trying to write a function to transform a data structure in the form:

input = [("a", [1,2,3]), ("b", [4,5,6])]

Into

output = [(a,1), (c,2) ..... (b,6)] 

My code is currently this:

    let foo=vec![('a', vec![1,2,3]), ('v', vec![2,3,4])];
    let baz: Vec<(char,i32)> = foo.into_iter().map(|a|a.1.into_iter().map( |b|(a.0, b))).flatten().collect();
    println!("{:?}",baz);

I'm getting this error:

error[E0373]: closure may outlive the current function, but it borrows `a`, which is owned by the current function
  --> src/lib.rs:10:76
   |
10 |     let baz: Vec<(char,i32)> = foo.into_iter().map(|a|a.1.into_iter().map( |b|(a.0, b))).flatten().collect();
   |                                                                            ^^^ - `a` is borrowed here
   |                                                                            |
   |                                                                            may outlive borrowed value `a`
   |
note: closure is returned here
  --> src/lib.rs:10:55
   |
10 |     let baz: Vec<(char,i32)> = foo.into_iter().map(|a|a.1.into_iter().map( |b|(a.0, b))).flatten().collect();
   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `a` (and any other referenced variables), use the `move` keyword
   |
10 |     let baz: Vec<(char,i32)> = foo.into_iter().map(|a|a.1.into_iter().map( move |b|(a.0, b))).flatten().collect();
   |                                                                            ^^^^^^^^

error[E0382]: borrow of moved value: `a`
  --> src/lib.rs:10:76
   |
10 |     let baz: Vec<(char,i32)> = foo.into_iter().map(|a|a.1.into_iter().map( |b|(a.0, b))).flatten().collect();
   |                                                       ---                  ^^^ - borrow occurs due to use in closure
   |                                                       |                    |
   |                                                       value moved here     value borrowed here after partial move
   |
   = note: move occurs because `a.1` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait

I think this means that Rust doesn't know how to copy my vector of i32s, so thinks it must move the vec instead, but cannot do it.

How do I fix this problem? Implement a Copy method for vec, or is there a niftier way to do this?

Upvotes: 1

Views: 682

Answers (2)

Aloso
Aloso

Reputation: 5387

When you call a.1.into_iter(), a is moved, and can't be borrowed in the inner closure anymore.

The easiest solution is to destructure a, so each component can be borrowed/moved individually:

.map(|(c, v)| v.into_iter().map(move |b| (c, b)))

Also note the move keyword, which means that c is moved into the inner closure, so it's allowed to outlive the outer closure.

Upvotes: 4

Kentaro Okuda
Kentaro Okuda

Reputation: 1582

IntoIterator consumes and yields values. Since Vec doesn’t implement Copy, when you call a.1.into_iter(), it is moved. You can clone it like this: a.1.clone().into_iter()

Also, you want to use the move keyword to take the ownership of a in the closure.

let baz: Vec<(char, i32)> = foo
    .into_iter()
    .map(|a| a.1.clone().into_iter().map(move |b| (a.0, b)))
    .flatten()
    .collect();
println!("{:?}", baz);
// [('a', 1), ('a', 2), ('a', 3), ('v', 2), ('v', 3), ('v', 4)]

Upvotes: 0

Related Questions