suyash
suyash

Reputation: 789

How to get ownership of a moved value back from a closure?

Consider the following program:

fn primes_up_to(n: usize) -> Vec<usize> {
    let mut ans = Vec::with_capacity(n);

    if n < 2 {
        return ans;
    }

    ans.push(2);

    // https://doc.rust-lang.org/1.22.0/book/first-edition/closures.html#closures-and-their-environment
    // The closure needs ownership of vec to access elements
    let is_prime = |n: usize| -> bool {
        for x in ans {
            if x * x > n {
                break;
            }

            if n % x == 0 {
                return false;
            }
        }

        true
    };

    let mut i = 3;
    while i <= n {
        if is_prime(i) {
            ans.push(i);
        }

        i += 2;
    }

    ans
}

fn main() {
    println!("{:?}", primes_up_to(23));
}

(playground)

Compiling the code above gives these compilation errors:

error[E0382]: use of moved value: `is_prime`
  --> src/main.rs:28:12
   |
28 |         if is_prime(i) {
   |            ^^^^^^^^ value moved here in previous iteration of loop
   |
note: closure cannot be invoked more than once because it moves the variable `ans` out of its environment
  --> src/main.rs:13:18
   |
13 |         for x in ans {
   |                  ^^^

error[E0382]: use of moved value: `ans`
  --> src/main.rs:29:13
   |
12 |     let is_prime = |n: usize| -> bool {
   |                    ------------------ value moved (into closure) here
...
29 |             ans.push(i);
   |             ^^^ value used here after move
   |
   = note: move occurs because `ans` has type `std::vec::Vec<usize>`, which does not implement the `Copy` trait

error[E0382]: use of moved value: `ans`
  --> src/main.rs:35:5
   |
12 |     let is_prime = |n: usize| -> bool {
   |                    ------------------ value moved (into closure) here
...
35 |     ans
   |     ^^^ value used here after move
   |
   = note: move occurs because `ans` has type `std::vec::Vec<usize>`, which does not implement the `Copy` trait

I know a potential solution would be to pass an immutable reference to ans (&ans) to is_prime. What is the idiomatic way for ownership of ans to pass into is_prime and come back out when is_prime exits every time I call is_prime?

Upvotes: 6

Views: 6472

Answers (2)

wasmup
wasmup

Reputation: 16273

There are some issues with your code, in:

for x in ans {

closure cannot be invoked more than once because it moves the variable ans out of its environment:

The iterator invalidates with each ans.push(i);

So you should borrow the ans exactly in this line:

if is_prime(i) {

then give it back to the next line:

ans.push(i);

The solution is to pass it as an argument:

fn primes_up_to(n: usize) -> Vec<usize> {
    let mut ans = Vec::with_capacity(n);
    if n < 2 {
        return ans;
    }
    ans.push(2);
    let is_prime = |n: usize, v: &[usize]| -> bool {
        for x in v {
            if x * x > n {
                break;
            }
            if n % x == 0 {
                return false;
            }
        }
        true
    };
    let mut i: usize = 3;
    while i <= n {
        if is_prime(i, &ans) {
            ans.push(i);
        }
        i += 2;
    }
    ans
}

fn main() {
    println!("{:?}", primes_up_to(23));
}

(playground)

Or try this, it has same ASM footprint as above in release mode on the nightly channel:

fn primes_up_to(n: usize) -> Vec<usize> {
    let mut ans = Vec::with_capacity(n);
    if n < 2 {
        return ans;
    }
    ans.push(2);
    let is_prime = |n: usize, v: &Vec<usize>| -> bool {
        for x in v {
            if x * x > n {
                break;
            }
            if n % x == 0 {
                return false;
            }
        }
        true
    };
    let mut i: usize = 3;
    while i <= n {
        if is_prime(i, &ans) {
            ans.push(i);
        }
        i += 2;
    }
    ans
}

fn main() {
    println!("{:?}", primes_up_to(23));
}

(playground)

output:

[2, 3, 5, 7, 11, 13, 17, 19, 23]

And see: Move semantics and Borrowing

Upvotes: 1

Shepmaster
Shepmaster

Reputation: 432049

How to get ownership of a moved value back from a closure

You don't.

Once ownership is transferred, it's gone. The only way to "get it back" is to have the thing that now owns it transfer it back. Some types have an into_inner method for this exact reason. Other types have equivalent methods.

A closure's only interface, by definition, is the ability to invoke it. There are no other possibilities for methods.

You could return the captured value when you are done, but that doesn't help in your case because you need to call the closure multiple times — you couldn't return it because it might be needed in the future.

Upvotes: 4

Related Questions