From 62f797ae3b574b84967005635c53c07be17ac7da Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 1 Apr 2025 17:53:08 -0500 Subject: [PATCH] i got bored with ch15 --- ch15/interior-mutability/Cargo.lock | 7 +++ ch15/interior-mutability/Cargo.toml | 6 +++ ch15/interior-mutability/src/lib.rs | 77 ++++++++++++++++++++++++++++ ch15/interior-mutability/src/main.rs | 73 ++++++++++++++++++++++++++ ch15/reference-cycles/Cargo.lock | 7 +++ ch15/reference-cycles/Cargo.toml | 6 +++ ch15/reference-cycles/src/main.rs | 42 +++++++++++++++ ch15/sect-rc/src/main.rs | 32 ++++++++++-- 8 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 ch15/interior-mutability/Cargo.lock create mode 100644 ch15/interior-mutability/Cargo.toml create mode 100644 ch15/interior-mutability/src/lib.rs create mode 100644 ch15/interior-mutability/src/main.rs create mode 100644 ch15/reference-cycles/Cargo.lock create mode 100644 ch15/reference-cycles/Cargo.toml create mode 100644 ch15/reference-cycles/src/main.rs diff --git a/ch15/interior-mutability/Cargo.lock b/ch15/interior-mutability/Cargo.lock new file mode 100644 index 0000000..7e6b789 --- /dev/null +++ b/ch15/interior-mutability/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "interior-mutability" +version = "0.1.0" diff --git a/ch15/interior-mutability/Cargo.toml b/ch15/interior-mutability/Cargo.toml new file mode 100644 index 0000000..777cc14 --- /dev/null +++ b/ch15/interior-mutability/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "interior-mutability" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch15/interior-mutability/src/lib.rs b/ch15/interior-mutability/src/lib.rs new file mode 100644 index 0000000..682223b --- /dev/null +++ b/ch15/interior-mutability/src/lib.rs @@ -0,0 +1,77 @@ +pub trait Messenger { + fn send(&self, msg: &str); +} + +pub struct LimitTracker<'a, T: Messenger> { + messenger: &'a T, + value: usize, + max: usize, +} + +impl<'a, T> LimitTracker<'a, T> +where + T: Messenger, +{ + pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> { + LimitTracker { + messenger, + value: 0, + max, + } + } + + pub fn set_value(&mut self, value: usize) { + self.value = value; + + let percentage_of_max = self.value as f64 / self.max as f64; + + if percentage_of_max >= 1.0 { + self.messenger.send("Error: over quota"); + } else if percentage_of_max >= 0.9 { + self.messenger.send("Urgent warning: above 90% of quota"); + } else if percentage_of_max >= 0.75 { + self.messenger.send("Warning: used over 75% of quota"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cell::RefCell; + + struct MockMessenger { + sent_messages: RefCell>, + } + + impl MockMessenger { + fn new() -> MockMessenger { + MockMessenger { + sent_messages: RefCell::new(vec![]), + } + } + } + + impl Messenger for MockMessenger { + fn send(&self, message: &str) { + // where the interior mutability comes into play + self.sent_messages.borrow_mut().push(String::from(message)); + + // These lines error for multiple mutable borrows + // let mut one_borrow = self.sent_messages.borrow_mut(); + // let mut two_borrow = self.sent_messages.borrow_mut(); + // one_borrow.push(String::from(message)); + // two_borrow.push(String::from(message)); + } + } + + #[test] + fn it_sends_an_over_75_percent_warning_message() { + let mock_messenger = MockMessenger::new(); + let mut limit_tracker = LimitTracker::new(&mock_messenger, 100); + + limit_tracker.set_value(80); + + assert_eq!(mock_messenger.sent_messages.borrow().len(), 1); + } +} diff --git a/ch15/interior-mutability/src/main.rs b/ch15/interior-mutability/src/main.rs new file mode 100644 index 0000000..ccc14ab --- /dev/null +++ b/ch15/interior-mutability/src/main.rs @@ -0,0 +1,73 @@ +//! # RefCell and the interior mutability pattern +//! +//! Interior mutability allows mutate data when immutable +//! references to said data exists. This is possible due to +//! the unsafe code. Unsafe code more in chapter 20. +//! +//! Can use types that use interior mutability pattern when +//! can ensure borrowing rules will be followed at runtime. +//! +//! ## Enforcing borrowing Rules at Runtime with RefCell +//! +//! RefCell represents single ownership over data it holds. +//! If the Rust compiler can't be sure the code compiles with +//! ownership rules, it might reject a correct program. +//! +//! A recap of reasons to choose Box, Rc, or RefCell: +//! 1) Rc enables multiple owners of the same data; Box +//! and RefCell have single owners. +//! 2) Box allows immutable or mutable borrows checked at +//! compile time; Rc allows only immutable borrows checked +//! at compile time; RefCell allos immutable or mutable +//! borrows checked at runtime. +//! 3) Because RefCell allows mutable borrows checked at +//! runtime, you can mutate the value inside the RefCell +//! even when the RefCell is immutable. +//! +//! Mutating the value inside an immutable value is the interior +//! mutability pattern. + +#[derive(Debug)] +enum List { + Cons(Rc>, Rc), + Nil, +} + +use crate::List::{Cons, Nil}; +use std::cell::RefCell; +use std::rc::Rc; + +fn main() { + // Interior mutability: A mutable borrow to an immutable value + + // this panic!s + // let x = 5; + // let y = &mut x; + + // A use case for interior mutability: mock objects + + // Sometimes use a type in place of another type, in order + // to observe particular behavior and assert it's implemented + // correctly. Called a test double. Stand in for other types + // when we're running tests. + // + // Mock objects are specific types of test doubles that record + // what happens during a test so can assert correct actions + // took place. + + // Having multiple owners of mutable data by combining Rc and RefCell + // Only gives immutable access to data in this fashion. + + let value = Rc::new(RefCell::new(5)); + + let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); + + let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); + let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); + + *value.borrow_mut() += 10; + + println!("a after = {a:?}"); + println!("b after = {b:?}"); + println!("c after = {c:?}"); +} diff --git a/ch15/reference-cycles/Cargo.lock b/ch15/reference-cycles/Cargo.lock new file mode 100644 index 0000000..19befb8 --- /dev/null +++ b/ch15/reference-cycles/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "reference-cycles" +version = "0.1.0" diff --git a/ch15/reference-cycles/Cargo.toml b/ch15/reference-cycles/Cargo.toml new file mode 100644 index 0000000..9618f0d --- /dev/null +++ b/ch15/reference-cycles/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "reference-cycles" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch15/reference-cycles/src/main.rs b/ch15/reference-cycles/src/main.rs new file mode 100644 index 0000000..e8b002f --- /dev/null +++ b/ch15/reference-cycles/src/main.rs @@ -0,0 +1,42 @@ +use crate::List::{Cons, Nil}; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug)] +enum List { + Cons(i32, RefCell>), + Nil, +} + +impl List { + fn tail(&self) -> Option<&RefCell>> { + match self { + Cons(_, item) => Some(item), + Nil => None, + } + } +} + +fn main() { + let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); + + println!("a initial rc count = {}", Rc::strong_count(&a)); + println!("a next item = {:?}", a.tail()); + + let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a)))); + + println!("a rc count after b creation = {}", Rc::strong_count(&a)); + println!("b initial rc count = {}", Rc::strong_count(&b)); + println!("b next item = {:?}", b.tail()); + + if let Some(link) = a.tail() { + *link.borrow_mut() = Rc::clone(&b); + } + + println!("b rc count after changing a = {}", Rc::strong_count(&b)); + println!("a rc count after changing a = {}", Rc::strong_count(&a)); + + // Uncomment the next line to see that we have a cycle; + // it will overflow the stack + // println!("a next item = {:?}", a.tail()); +} diff --git a/ch15/sect-rc/src/main.rs b/ch15/sect-rc/src/main.rs index d931b69..6247ffc 100644 --- a/ch15/sect-rc/src/main.rs +++ b/ch15/sect-rc/src/main.rs @@ -1,6 +1,32 @@ -//! Stopped here: -//! https://rust-book.cs.brown.edu/ch15-04-rc.html#rct-the-reference-counted-smart-pointer +//! # Rc, the Reference Counted Smart Pointer +//! +//! For cases when a single value might have multiple owners. +//! Rc keeps track of number of references to a value +//! to determine whether or not the value is still in use. +//! Rc only for use in single-threaded environments. + +enum List { + Cons(i32, Rc), + Nil, +} + +use crate::List::{Cons, Nil}; +use std::rc::Rc; fn main() { - println!("Hello, world!"); + let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); + let b = Cons(3, Rc::clone(&a)); + let c = Cons(4, Rc::clone(&a)); + + // cloning an Rc increases the reference count + // Can count the number of references with Rc::strong_count(&a) + let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); + println!("count after creating a = {}", Rc::strong_count(&a)); + let b = Cons(3, Rc::clone(&a)); + println!("count after creating b = {}", Rc::strong_count(&a)); + { + let c = Cons(4, Rc::clone(&a)); + println!("count after creating c = {}", Rc::strong_count(&a)); + } + println!("count after c goes out of scope = {}", Rc::strong_count(&a)); }