Closure

  • Unlike functions, Rust supports type inference for closure - type annotation is optional.
  • Closures can capture values from their environment in three ways:
    • borrowing immutably
    • borrowing mutably
    • taking ownership
  • Depending on what the body of the function does with the captured values, closure will decide which one of above three to be used.

Borrowing immutably

fn main() {
    // Rust allows multiple immutable references
    let list = vec![1, 2, 3];
    // list is immutably borrowed here
    let immutable_borrow = || println!("borrowed list: {:?}", list);
    println!("before immutable borrow: {:?}", list);
    immutable_borrow();
    println!("before immutable borrow: {:?}", list);
    immutable_borrow();
    println!("before immutable borrow: {:?}", list);
    immutable_borrow();
    println!("after all borrow, list is {:?}", list);
}

Borrowing mutably

fn main() {
    let mut list = vec![1, 2, 3];
    let mut mutable_borrow = || list.push(4);
    mutable_borrow();
    mutable_borrow();
    println!("after mutable borrow, list is {:?}", list);
}

Closure ownership

Use move keyword to force closure to take ownership of its environment values

use std::thread;

fn main() {
    let mut list = vec![1, 2, 3];
    println!("before mutable borrow: {:?}", list);
   
    thread::spawn(move || println!("From thread: {:?}", list))
    .join()
    .unwrap();
}

For copy type, closure will copy it.

fn main() {
    let a = 10;
    let move_closure = move |x : i32| a + x;
    println!("{}", move_closure(2));
    println!("a is still here {a}");
}

Sometimes, a closure must take ownership of an environment variable to be valid and it happens automatically without move.

// magic_string is immutably borrowed here
fn main() {
    let magic_string = String::from("abracadaba");
    let closure = || println!("{:?}", magic_string);
    closure();
    println!("{:?}", magic_string);
}
// magic_string is moved here and 
// closure takes ownership without needing to use 'move' keyword
fn main() {
    let magic_string = String::from("abracadaba");
    let _closure = || magic_string;
    println!("{:?}", magic_string); // throw error here
}

A closure that take ownership of environment variable(s) can be called only once.