Closure Environment

Borrow or Move

Rust determines whether a variable is borrowed or moved from the closure environment when capturing the variables.

  • Inferred borrow:

    #[derive(Clone, Debug)]
    struct A {
        value: i32,
    }
    
    fn borrow(_a: &A) { println!("borrow"); }
    fn consume(_a: A) { println!("consume"); }
    
    fn main() {
        let a = A { value: 0 };
        let closure = || {
            // `a` still refers to `A` instead of `&A`.
            // So we have to add `&` in front of it.
    
        //  borrow(a);
        //         ^
        //         |
        //         expected &A, found struct `A`
        //         help: consider borrowing here: `&a`
    
            borrow(&a);
        };
        closure();
    
        let closure_move = || consume(a);
        closure_move();
    }
    
  • Inferred move:

    #[derive(Clone, Debug)]
    struct A {
        value: i32,
    }
    
    fn borrow(_a: &A) { println!("borrow"); }
    fn consume(_a: A) { println!("consume"); }
    
    fn main() {
        let a = A { value: 0 };
        let closure = || {
        //            -- value moved into closure here
            borrow(&a);
            consume(a);
        };
        closure();
    
        // let closure_move = || consume(a);
        //                    ^^         - use occurs due to use in closure
        //                    |
        //                    value used here after move
        // closure_move();
    }
    
  • Explicit move:

    You can use the move keyword to indicate the variable must be moved instead of borrowed.

    #![allow(dead_code)]
    
    #[derive(Clone, Debug)]
    struct A {
        value: i32,
    }
    
    fn borrow(_a: &A) {}
    fn consume(_a: A) {}
    
    fn main() {
        let a = A { value: 0 };
    
        let closure_move = move || borrow(&a);
        //                 -------         - variable moved due to use in closure
        //                 |
        //                 value moved into closure here
        closure_move();
    
        // let closure_move = move || consume(a);
        //                    ^^^^^^^         - use occurs due to use in closure
        //                    |
        //                    value used here after move
        // closure_move();
    }
    

Borrow Lifetime

The usual borrowing rules apply -- note that the borrow lasts for the lifetime of the closure:

#[derive(Debug)]
struct A {
    value: i32,
}

fn main() {
    let mut a = A { value: 0 };
    let mut closure = || a.value = 123;
    //                -- - borrow occurs due to use in closure
    //                |
    //                borrow of `a.value` occurs here

    // a.value = 456; // error[E0506]: cannot assign to `a.value` because it is borrowed

    closure();
//  ------- borrow later used here
}