202 lines
6.1 KiB
Rust

// 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<ShirtColor>,
}
impl Inventory {
fn giveaway(&self, user_preference: Option<ShirtColor>) -> 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<T> Option<T> {
// pub fn unwrap_or_else<F>(self, f: F) -> T
// where
// F: FnOnce() -> T,
// {
// match self {
// Some(x) => x,
// None => f(),
// }
// }
// }