Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

capturing in closures in Rust '21

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
}
like image 816
vikram2784 Avatar asked Mar 18 '26 13:03

vikram2784


1 Answers

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".

like image 77
pizzamonkey Avatar answered Mar 20 '26 16:03

pizzamonkey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!