Do we need `move` to capture something that is `Copy`?

I know what move is for and it makes sense to me, but I am puzzled by the behavior of capturing something that is Copy.

For instance the following code compiles

#[derive(Debug, Clone, Copy)]
struct MyType (usize);

fn take<T>(t: T) {}

fn main() {
    let v = MyType(0);
    let ok = || take(v);
    ok();
    println!("Hello, world! {:?}", v);
}

This makes perfect sense since we will never move MyType but always copy. And removing the Copy trait results in compilation error.

However the following code doesn't compile

fn main() {
    let pairs = vec![(1, 2), (2, 3), (3, 4)];
    let x = vec![1, 2, 3];
    let wtf = pairs.iter().flat_map(|&(from, to)| x.iter().map(|_| from));
}

The compiler complains that I borrowed from and suggests me to add the move keyword. I added it and it compiles.

fn main() {
    let pairs = vec![(1, 2), (2, 3), (3, 4)];
    let x = vec![1, 2, 3];
    let wtf = pairs.iter().flat_map(|&(from, to)| x.iter().map(move |_| from));
}

This really puzzled me, when do we need the move keyword? Why does the first one compiles but not the second one? Please correct me if I understand anything wrong, thank you very much!

Permalink to the playground

Ah, I guess I see why, in the first example I call take() which forced the copy?

That one captures by reference too, and makes a copy inside the closure, because capturing by shared reference is enough to perform the copy:

fn main() {
    let mut v = MyType(0);
    let ok = || take(v);
    let _ = &mut v;  // <-- new (and incompatible with being borrowed)
    ok();
    println!("Hello, world! {:?}", v);
}

(Printing also only requires a shared borrow, so your OP compiled.)

I'd say see this section of the reference... and the first paragraph is still the pertitent part, but the rest is incomplete and out of date.

  • Closure context, such as being an inline argument to a function expecting a closure that meets certain bounds, can "override" the compiler's capturing "preferences"
  • Borrows of individual fields can be captured (since edition 2021; run on edition 2018 to see the failure)
1 Like

Thank you for the quick reply, what would happen if I want to borrow something from the environment and also copy something from the environment? It seems that if I use move keywords everything will be either move or copy but if I don't use move keyword I will run into the issue in this post.

Put a borrow in a local variable, and move that instead.

2 Likes

Ah, that's smart, thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.