// Closures are anonymous functions. Can save in variables or pass // to other functions. Closures can capture the values from the scope // they're defined in. use std::thread; use std::time::Duration; #[derive(Debug, PartialEq, Copy, Clone)] enum ShirtColor { Red, Blue, } struct Inventory { shirts: Vec, } impl Inventory { fn giveaway(&self, user_preference: Option) -> ShirtColor { user_preference.unwrap_or_else(|| self.most_stocked()) } fn most_stocked(&self) -> ShirtColor { let mut num_red = 0; let mut num_blue = 0; for color in &self.shirts { match color { ShirtColor::Red => num_red += 1, ShirtColor::Blue => num_blue += 1, } } if num_red > num_blue { ShirtColor::Red } else { ShirtColor::Blue } } } #[derive(Debug)] struct Rectangle { width: u32, height: u32, } fn main() { let store = Inventory { shirts: vec![ShirtColor::Blue, ShirtColor::Red, ShirtColor::Blue], }; let user_pref1 = Some(ShirtColor::Red); let giveaway1 = store.giveaway(user_pref1); println!( "The user with preference {:?} gets {:?}", user_pref1, giveaway1 ); let user_pref2 = None; let giveaway2 = store.giveaway(user_pref2); println!( "The user with preference {:?} gets {:?}", user_pref2, giveaway2 ); // closure type inference and annotation let expensive_closure = |num: u32| -> u32 { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }; // similarity between closures and functions fn add_one_v1(x: u32) -> u32 { x + 1 } let add_one_v2 = |x: u32| -> u32 { x + 1 }; // capturing references or moving ownership // closures capture values from env in 3 ways // 1) borrowing immutably // 2) borrowing mutably // 3) taking ownership let list = vec![1, 2, 3]; println!("Before defining closure: {list:?}"); let only_borrows = || println!("From closure: {list:?}"); println!("Before calling closure: {list:?}"); only_borrows(); println!("After calling closure: {list:?}"); // change closure body to push instead of print let mut list = vec![1, 2, 3, 4]; println!("Before defining closure: {list:?}"); let mut borrows_mutably = || list.push(7); // println!("Before calling closure: {list:?}"); borrows_mutably(); println!("After calling closure: {list:?}"); // If want to force closure to take ownership of values it uses even // tho closure doesn't strictly need ownership, use the `move` keyword let list = vec![1, 2, 3]; println!("Before defining closure: {list:?}"); thread::spawn(move || println!("From thread: {list:?}")) .join() .unwrap(); // Moving captured values out of the closures and the Fn traits // Once a closure has captured a reference or captured ownership of a value // from the env where closure is defined (affecting what is moved into the // closure), the code in the body of the closure defines what happens to // the references or values when the closure is evaluated later (affecting // what is moved out of the closure). Closure body can do any of the following: // 1) move captured value out of the closure // 2) mutate captured value // 3) neither move nor mutate the value // 4) capture nothing from the env to begin with // // Closures will automatically implement some or all of the following // traits in an additive fashing depending on the closure's body // handles the values // 1) FnOnce applies to all closures that can be called once. // All closures implement at least this trait. A closure that // moves captured values out of its body will only implement // FnOnce and one of the other Fn traits, because it can only // by called once. // 2) FnMut applies to closures that don't move captured values out // of their body but might mutate the captured values. These // closures can't be called more than once. // 3) Fn applies to closures that don't move captured values out of // their body and that don't mutate captured values, and closures // that capture nothing from their environment. These closures can // be called more than once without mutating their environment. This // is nice for calling a closure multiple times concurrently. // sort_by_key uses FnMut let mut list = [ Rectangle { width: 10, height: 1, }, Rectangle { width: 3, height: 5, }, Rectangle { width: 7, height: 12, }, ]; // let mut sort_operations = vec![]; // let value = String::from("closure called"); let mut num_sort_operations = 0; // list.sort_by_key(|r| r.width); list.sort_by_key(|r| { // sort_operations.push(value); num_sort_operations += 1; r.width }); println!("{list:#?}"); // closures must name captured lifetimes // let s_own = String::from("Hello world"); // let cloner = make_a_cloner(&s_own); // drop(s_own); // cloner(); } // from closures must name captured lifetimes // Need to add a lifetime to tell Rust that the closure // returned from make_a_cloner must not live longer than s_ref. // The `+ 'a` in the return type indicates that the closure // must live no longer than 'a. // fn make_a_cloner<'a>(s_ref: &'a str) -> impl Fn() -> String + 'a { // move || s_ref.to_string() // } // Can make this function more concise with the lifetime elision rules fn make_a_cloner(s_ref: &str) -> impl Fn() -> String + '_ { move || s_ref.to_string() } // The implementation for unwrap_or_else // Uses FnOnce // impl Option { // pub fn unwrap_or_else(self, f: F) -> T // where // F: FnOnce() -> T, // { // match self { // Some(x) => x, // None => f(), // } // } // }