almost done w/ ch13.01

This commit is contained in:
Rowan Torbitzky-Lane 2025-03-31 09:43:16 -05:00
parent 83d8d2245c
commit 6010b12653
4 changed files with 219 additions and 0 deletions

7
ch13/closures/Cargo.lock generated Normal file
View File

@ -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"

6
ch13/closures/Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "closures"
version = "0.1.0"
edition = "2021"
[dependencies]

14
ch13/closures/src/lib.rs Normal file
View File

@ -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);
}
}

192
ch13/closures/src/main.rs Normal file
View File

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