i got bored with ch15
This commit is contained in:
parent
6597cebeba
commit
62f797ae3b
7
ch15/interior-mutability/Cargo.lock
generated
Normal file
7
ch15/interior-mutability/Cargo.lock
generated
Normal 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"
|
6
ch15/interior-mutability/Cargo.toml
Normal file
6
ch15/interior-mutability/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "interior-mutability"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
77
ch15/interior-mutability/src/lib.rs
Normal file
77
ch15/interior-mutability/src/lib.rs
Normal 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);
|
||||
}
|
||||
}
|
73
ch15/interior-mutability/src/main.rs
Normal file
73
ch15/interior-mutability/src/main.rs
Normal 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
7
ch15/reference-cycles/Cargo.lock
generated
Normal 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"
|
6
ch15/reference-cycles/Cargo.toml
Normal file
6
ch15/reference-cycles/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "reference-cycles"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
42
ch15/reference-cycles/src/main.rs
Normal file
42
ch15/reference-cycles/src/main.rs
Normal 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());
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user