i got bored with ch15

This commit is contained in:
Rowan Torbitzky-Lane 2025-04-01 17:53:08 -05:00
parent 6597cebeba
commit 62f797ae3b
8 changed files with 247 additions and 3 deletions

7
ch15/interior-mutability/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 = "interior-mutability"
version = "0.1.0"

View File

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

View File

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

View File

@ -0,0 +1,73 @@
//! # RefCell<T> 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<T>
//!
//! RefCell<T> 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<T>, Rc<T>, or RefCell<T>:
//! 1) Rc<T> enables multiple owners of the same data; Box<T>
//! and RefCell<T> have single owners.
//! 2) Box<T> allows immutable or mutable borrows checked at
//! compile time; Rc<T> allows only immutable borrows checked
//! at compile time; RefCell<T> allos immutable or mutable
//! borrows checked at runtime.
//! 3) Because RefCell<T> allows mutable borrows checked at
//! runtime, you can mutate the value inside the RefCell<T>
//! even when the RefCell<T> is immutable.
//!
//! Mutating the value inside an immutable value is the interior
//! mutability pattern.
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
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<T> and RefCell<T>
// 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:?}");
}

7
ch15/reference-cycles/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 = "reference-cycles"
version = "0.1.0"

View File

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

View File

@ -0,0 +1,42 @@
use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
enum List {
Cons(i32, RefCell<Rc<List>>),
Nil,
}
impl List {
fn tail(&self) -> Option<&RefCell<Rc<List>>> {
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());
}

View File

@ -1,6 +1,32 @@
//! Stopped here:
//! https://rust-book.cs.brown.edu/ch15-04-rc.html#rct-the-reference-counted-smart-pointer
//! # Rc<T>, the Reference Counted Smart Pointer
//!
//! For cases when a single value might have multiple owners.
//! Rc<T> keeps track of number of references to a value
//! to determine whether or not the value is still in use.
//! Rc<T> only for use in single-threaded environments.
enum List {
Cons(i32, Rc<List>),
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<T> 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));
}