Search code examples
rustclosures

Understanding Rust Closures: Why do they Continuously Hold Mutable References?


I'm currently learning about closures in Rust and am trying to understand why they continuously hold references until their last use.

Consider the following code:

fn borrows_mutably_func(list: &mut Vec<i32>) {
    list.push(4);
}

fn main() {
    let mut list = vec![1, 2, 3];
    println!("Before defining: {:?}", list);


    borrows_mutably_func(&mut list);
    println!("After calling first (func): {:?}", list);
    borrows_mutably_func(&mut list);
    println!("After calling second (func): {:?}", list);
    
    
    let mut borrows_mutably_closure = || list.push(5);
    
    borrows_mutably_closure();
    println!("After calling first (closure): {:?}", list);
    borrows_mutably_closure();
    println!("After calling second (closure): {:?}", list);
}

In this code, borrows_mutably_func is a function that mutates list by adding an element to it. This function can be called multiple times without any issues. However, when I try to replicate this behavior using a closure (borrows_mutably_closure), the code fails to compile.

From my understanding, this issue arises because the closure captures list by a mutable reference and holds onto this reference continuously until its last use. This differs from the function, which releases the reference after each call.

My question is: Why does Rust enforce this behavior for closures? Why can't the closure release the mutable reference after its first use (like the function does) and re-acquire it just before its second use? This approach seems more intuitive to me, as it would allow the closure to behave similarly to the function without continuously holding the mutable reference. Any insights would be greatly appreciated!


Solution

  • My question is: Why does Rust enforce this behavior for closures? Why can't the closure release the mutable reference after its first use (like the function does) and re-acquire it just before its second use?

    Because it's not feasible. A closure is, in essence, a structure which stores all the things it closes over. So here on line 13 you essentially instantiate a structure similar to:

    struct BorrowsMutablyClosure<'a> {
        list: &'a mut Vec<i32> 
    }
    impl BorrowsMutablyClosure<'_> {
        fn call_mut(&mut self) {
            self.list.push(5);
        }
    }
    

    The function is a completely different case, you pass in the reference during each call, that's how the reference is available to the function.

    You can do the exact same thing with the closure, if you pass in the reference as parameter, it won't need to store the reference internally.

    This approach seems more intuitive to me

    But it makes no sense. The mutable reference is necessarily outstanding from the instant you create the closure to the instant the closure is destroyed, since the closure needs to store it. There is no mecanism for the closure to release then re-acquire a mutable reference.