1

I think these two methods are equivalent, but there is an error. What is the reason? Is there a better way to express it?

pub fn create_pair() -> () {
    let vec_num = vec![1, 2, 3];
    let vec_num2 = &vec_num;
    let all = &vec_num
        .iter()
        .flat_map(move |a| vec_num2.iter().map(move |b| [a, b]))
        .collect::<Vec<_>>();

    println!("{:?}", all);
}
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
pub fn create_pair() -> () {
    let vec_num = vec![1, 2, 3];
    let all = &vec_num
        .iter()
        .flat_map(move |a| &vec_num.iter().map(move |b| [a, b]))
        .collect::<Vec<_>>();

    println!("{:?}", all);
}
error[E0277]: `&std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>` is not an iterator
 --> src/main.rs:5:10
  |
5 |         .flat_map(move |a| &vec_num.iter().map(move |b| [a, b]))
  |          ^^^^^^^^ `&std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>` is not an iterator
  |
  = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>`
  = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>`

error[E0599]: no method named `collect` found for struct `std::iter::FlatMap<std::slice::Iter<'_, {integer}>, &std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>, [closure@src/main.rs:5:19: 5:64 vec_num:_]>` in the current scope
  --> src/main.rs:6:10
   |
6  |           .collect::<Vec<_>>();
   |            ^^^^^^^ method not found in `std::iter::FlatMap<std::slice::Iter<'_, {integer}>, &std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>, [closure@src/main.rs:5:19: 5:64 vec_num:_]>`
   |
   = note: the method `collect` exists but the following trait bounds were not satisfied:
           `&std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>: std::iter::IntoIterator`
           which is required by `std::iter::FlatMap<std::slice::Iter<'_, {integer}>, &std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>, [closure@src/main.rs:5:19: 5:64 vec_num:_]>: std::iter::Iterator`
           `std::iter::FlatMap<std::slice::Iter<'_, {integer}>, &std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>, [closure@src/main.rs:5:19: 5:64 vec_num:_]>: std::iter::Iterator`
           which is required by `&mut std::iter::FlatMap<std::slice::Iter<'_, {integer}>, &std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:5:48: 5:63 a:_]>, [closure@src/main.rs:5:19: 5:64 vec_num:_]>: std::iter::Iterator`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Squallcx
  • 115
  • 10
  • It looks like your question might be answered by the answers of [Why can't I use `&Iterator` as an iterator?](https://stackoverflow.com/q/51758485/155423) and [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/q/32682876/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Sep 29 '20 at 01:22
  • 1
    The second `create_pair` has multiple problems. First, the [precedence of `&` is lower than `.`](/q/49946620/3650362), so `&vec_num.iter()` isn't behaving like you expect. Second, [`move` causes the value of any captured variable to be moved](/q/64032329/3650362), so by writing `vec_num` inside a `move` closure you try to move `vec_num` *into* the closure, but `vec_num` is already borrowed by the temporary iterator. Finally, inside the closure you iterate over `vec_num` by reference and return the iterator, which means you're trying to return references to a local from a function. – trent Sep 29 '20 at 02:22

1 Answers1

2

According to the Rust reference, the & operator has a lower precedence than a method call. So this closure:

move |a| &vec_num.iter().map(move |b| [a, b])

would be first trying to calculate vec_num.iter().map(move |b| [a, b]), and then take a reference to that result, and return it. That is not at all equivalent to your first sample.

I have a hunch that you meant this instead:

move |a| (&vec_num).iter().map(move |b| [a, b])

That does not work either, but for completely different reasons:

error: captured variable cannot escape `FnMut` closure body
  --> src/main.rs:17:28
   |
14 |     let vec_num = vec![1, 2, 3];
   |         ------- variable defined here
...
17 |         .flat_map(move |a| (&vec_num).iter().map(move |b| [a, b]))
   |                          - ^^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                          | | |
   |                          | | variable captured here
   |                          | returns a reference to a captured variable which escapes the closure body
   |                          inferred to be a `FnMut` closure
   |
   = note: `FnMut` closures only have access to their captured variables while they are executing...
   = note: ...therefore, they cannot allow references to captured variables to escape

error[E0505]: cannot move out of `vec_num` because it is borrowed
  --> src/main.rs:17:19
   |
15 |     let all = &vec_num
   |                ------- borrow of `vec_num` occurs here
16 |         .iter()
17 |         .flat_map(move |a| (&vec_num).iter().map(move |b| [a, b]))
   |          -------- ^^^^^^^^   ------- move occurs due to use in closure
   |          |        |
   |          |        move out of `vec_num` occurs here
   |          borrow later used by call

error: aborting due to 2 previous errors

You can't both borrow vec_num for a method call, at the same time as moving it into the closure. And, since the closure now owns vec_num, you can't return a value that ultimately contains a reference to vec_num, because it would be dropped as soon as you returned from the closure.

If you take away the move, then the closure will borrow vec_num instead of consuming it.

Again, according to the Rust Reference:

Closures can capture variables:

  • by reference: &T
  • by mutable reference: &mut T
  • by value: T

They preferentially capture variables by reference and only go lower when required.

You don't need a & to indicate that variables should be captured by reference. This works:

pub fn main() -> () {
    let vec_num = vec![1, 2, 3];
    let all = &vec_num
        .iter()
        .flat_map(|a| vec_num.iter().map(move |b| [a, b]))
        .collect::<Vec<_>>();

    println!("{:?}", all);
}
harmic
  • 28,606
  • 5
  • 67
  • 91