I just found out that the following code compiles in Rust 21 (used to not compile in 18)
fn get_func (i: &mut i32) -> impl Fn() -> i32 + '_ {
|| *i
}
Is there an implicit move of i involved ? If so, then why does the following code compile too?
fn get_func (i: &mut i32) -> impl Fn() -> i32 + '_ {
let f = || *i;
println!("{:?}", i); // was expecting here to give borrow of moved variable error, `&mut` doesnt implement `Copy` trait
f
}
Or instead is it implicitly moving (copying in this case) the value being pointed to? But then the following code should compile, which doesn't -- indicating it's moving the reference.
fn get_func (i: &mut i32) -> impl Fn() -> i32 {
|| *i
}
It's downgrading your &mut to a & and making as many copies as it needs to.
This is all a byproduct of the fact that &mut has all the permissions of &; in other words it's fine to downgrade from a mutable reference to a non-mutable reference, and it's fine to copy a non-mutable reference as many times as you want, so this is just doing both of those implicitly.
fn get_func (i: &mut i32) -> impl Fn() -> i32 + '_ {
*i += 1; // fine, still mutable
let f = || *i;
println!("{}", *i); // fine, no mutability required
*i += 1; // ERROR: cannot mutate *i while borrowed by f
f
}
It's worth noting that if you actually do try to capture it as mutable, this happens.
fn get_func (i: &mut i32) -> impl FnMut() -> i32 + '_ {
println!("{}", *i); // Fine
*i += 1; // Fine
let f = || {*i += 1; *i};
println!("{}", *i); // ERROR: cannot borrow *i as immutable
f
}
Also the lifetime doesn't refer to the return of the closure, but the closure itself. Because no memory is moved, the closure is only valid so long as the memory at i's address remains valid. That's why removing the lifetime makes the code not compile.
In any case, all you really have to remember is the classic: "one mutable reference OR multiple immutable references, not both".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With