diff --git a/ch13/closures/Cargo.lock b/ch13/closures/Cargo.lock new file mode 100644 index 0000000..adc103d --- /dev/null +++ b/ch13/closures/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "closures" +version = "0.1.0" diff --git a/ch13/closures/Cargo.toml b/ch13/closures/Cargo.toml new file mode 100644 index 0000000..2cb7c6f --- /dev/null +++ b/ch13/closures/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "closures" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch13/closures/src/lib.rs b/ch13/closures/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/ch13/closures/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/ch13/closures/src/main.rs b/ch13/closures/src/main.rs new file mode 100644 index 0000000..0616cd7 --- /dev/null +++ b/ch13/closures/src/main.rs @@ -0,0 +1,192 @@ +// 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 + + // Stopped here: + // https://rust-book.cs.brown.edu/ch13-01-closures.html#closures-must-name-captured-lifetimes +} + +// from closures must name captured lifetimes +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(), +// } +// } +// }