Reputation: 789
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));
}
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
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));
}
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));
}
output:
[2, 3, 5, 7, 11, 13, 17, 19, 23]
And see: Move semantics and Borrowing
Upvotes: 1
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