I've been following this thread, and learning from it:
The following code, based on that thread, fails to compile because nums is mutably borrowed by the time the compiler sees the new tuple being created:
fn fib() -> impl Iterator<Item = u32> {
let mut nums = (0, 1);
std::iter::repeat_with(
move || {
std::mem::replace(&mut nums, (nums.1, nums.0 + nums.1)).0
}
)
}
fn main() {
dbg!(fib().take(6).collect::<Vec<u32>>());
dbg!(fib()
.take_while(|&x| x < 100)
.filter(|x| x % 2 == 0)
.sum::<u32>()
);
}
error[E0503]: cannot use `nums.1` because it was mutably borrowed
--> src/main.rs:13:43
|
13 | std::mem::replace(&mut nums, (nums.1, nums.0 + nums.1)).0
| ----------------- --------- ^^^^^^ use of borrowed `nums`
| | |
| | borrow of `nums` occurs here
| borrow later used by call
But if the arguments were reversed, it would work:
// replace, but with the arguments reversed
fn swap<T>(src: T, dest: &mut T) -> T {
std::mem::replace(dest, src)
}
fn fib() -> impl Iterator<Item = u32> {
let mut nums = (0, 1);
std::iter::repeat_with(
move || {
swap((nums.1, nums.0 + nums.1), &mut nums).0 // this works
}
)
}
Given that the target is mutable, it seems unfortunate that it appears first in the argument list.
But hmmm -- it doesn't really feel like it should make any difference. Despite their lexical order, argument evaluation precedes the actual call, so conceptually the new tuple is created first, right? Shouldn't the borrow checker look at it that way?
EDIT: Just to be clear, I am not suggesting that the order of argument evaluation be changed. I am asking whether the borrow checker should only consider the mutable borrow to occur at the time the function call is made, after argument marshaling. The function might mutate the variable, but the marshaling doesn't. I don't know why the mutable borrow would need to be in effect during argument marshaling.