Rust8.1 Smart Pointers
Rust学习笔记
Rust编程语言入门教程课程笔记
参考教材: The Rust Programming Language (by Steve Klabnik and Carol Nichols, with contributions from the Rust Community)
Lecture 15: Smart Pointers
src/main.rs
use crate::List::{Cons, Nil};
use std::ops::Deref;
use crate::RcList::{RcCons, RcNil};
use std::rc::Rc;
use std::cell::RefCell;
use crate::List2::{Cons2, Nil2};
use crate::List3::{Cons3, Nil3};
use std::rc::Weak;fn main() {//reference counting//Rc<T> allows multiple ownership of immutable data//Example: String and Vec<T>//Trait//Deref: allows an instance of a smart pointer to behave like a reference//Drop: allows you to customize the code that is run when an instance of the smart pointer goes out of scope//The most common smart pointers in the standard library//Box<T> for allocating values on the heap//Rc<T>, a reference counting type that enables multiple ownership//Ref<T> and RefMut<T>, accessed through RefCell<T>, a type that enforces the borrowing rules at runtime instead of compile time//Box<T>let b = Box::new(5);println!("b = {}", b);//Recursive Typeslet _list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));//Deref Trait//If a struct implements the Deref trait, we can use the * operator//to dereference an instance of that struct//Rust will analyze the types and use Deref::deref as many times as necessary//to get a reference to match the parameter's typelet x = 5;let y = &x;let z = Box::new(x);let zz = MyBox::new(x);assert_eq!(5, x);assert_eq!(5, *y);assert_eq!(5, *z);assert_eq!(5, *zz);//Deref coercion//Rust does deref coercion when it finds types and trait implementations in three cases://From &T to &U when T: Deref<Target=U>let m = MyBox::new(String::from("Rust"));hello(&m);//hello(&(*m)[..]);hello("Rust");//Drop Trait //in the preludelet c = CustomSmartPointer { data: String::from("my stuff") };drop(c);//force a value to be dropped sooner than the end of its scopelet d = CustomSmartPointer { data: String::from("other stuff") };println!("CustomSmartPointers created.");//Output://CustomSmartPointers created.//Dropping CustomSmartPointer with data `other stuff`!//Dropping CustomSmartPointer with data `my stuff`!//Rust automatically called drop for us when our instances went out of scope, //calling the code we specified. //Variables are dropped in the reverse order of their creation, so d was dropped before c.//Rc<T> Reference Counted Smart Pointer//Rc<T> enables multiple owners of the same data; Box<T> and RefCell<T> have single owners.//Rc<T> keeps track of the number of references to a value which determines whether or not a value is still in use.//If there are zero references to a value, the value can be cleaned up without any references becoming invalid.//Rc<T> is only for use in single-threaded scenarios.//Rc<T> is only for use in single-threaded scenarioslet list_a = Rc::new(RcCons(5, Rc::new(RcCons(10, Rc::new(RcNil)))));println!("count after creating list_a = {}", Rc::strong_count(&list_a));//1let list_b = RcCons(3, Rc::clone(&list_a));//Rc::clone doesn't make a deep copy of all the data like most types' implementations of clone do.println!("count after creating list_b = {}", Rc::strong_count(&list_a));//2{let list_c = RcCons(4, Rc::clone(&list_a));println!("count after creating list_c = {}", Rc::strong_count(&list_a));//3}println!("count after list_c goes out of scope = {}", Rc::strong_count(&list_a));//2//RefCell<T> and the Interior Mutability Pattern//RefCell<T> is useful when you’re sure your code follows the borrowing rules but the compiler is unable to understand and guarantee that.//RefCell<T> can only be used in single-threaded scenarios.let value = Rc::new(RefCell::new(5));let a = Rc::new(Cons2(Rc::clone(&value), Rc::new(Nil2)));let b = Cons2(Rc::new(RefCell::new(6)), Rc::clone(&a));let c = Cons2(Rc::new(RefCell::new(10)), Rc::clone(&a));*value.borrow_mut() += 10;//borrow_mut returns a RefMut<T> smart pointerprintln!("a after = {:?}", a);println!("b after = {:?}", b);println!("c after = {:?}", c);//Other Smart Pointers//Cell<T>: a type that internally uses RefCell<T> but also can be copied//Mutex<T>: a type of smart pointer that locks access to the inner data using a mutex//Reference Cycles Can Leak Memory//Rc<T> allows you to have multiple owners of some data, but it doesn’t let you mutate that data.//If we want to mutate data, we need to use the interior mutability pattern.//RefCell<T> allows us to mutate contents inside an Rc<T>.//RefCell<T> keeps track of how many Ref<T> and RefMut<T> smart pointers are currently active.//When either kind of smart pointer is dropped, RefCell<T> will decrease the count of the number of smart pointers that are active.//When the count of either goes back to zero, the RefCell<T> will reclaim its inner value.let a = Rc::new(Cons3(5, RefCell::new(Rc::new(Nil3))));println!("a initial rc count = {}", Rc::strong_count(&a));//1println!("a next item = {:?}", a.tail());//Some(RefCell { value: Nil3 })let b = Rc::new(Cons3(10, RefCell::new(Rc::clone(&a))));println!("a rc count after b creation = {}", Rc::strong_count(&a));//2println!("b initial rc count = {}", Rc::strong_count(&b));//1println!("b next item = {:?}", b.tail());//Some(RefCell { value: Cons3(5, RefCell { value: Nil3 }) })if let Some(link) = a.tail() {*link.borrow_mut() = Rc::clone(&b);} println!("b rc count after changing a = {}", Rc::strong_count(&b));//2println!("a rc count after changing a = {}", Rc::strong_count(&a));//2//println!("a next item = {:?}", a.tail());//Some(RefCell { value: Cons3(10, RefCell { value: Cons3(5, RefCell { value: Cons3(10, RefCell { value: Nil3 }) }) }) })//Weak Referenceslet leaf = Rc::new(Node {value: 3,parent: RefCell::new(Weak::new()),//Weak::new() creates a Weak<T> that doesn’t have an ownerchildren: RefCell::new(vec![]),});println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));//1, 0println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());//None{let branch = Rc::new(Node {value: 5,parent: RefCell::new(Weak::new()),//Weak::new() creates a Weak<T> that doesn’t have an ownerchildren: RefCell::new(vec![Rc::clone(&leaf)]),});*leaf.parent.borrow_mut() = Rc::downgrade(&branch);//Rc::downgrade creates a Weak<T> from a Rc<T> referenceprintln!("branch strong = {}, weak = {}", Rc::strong_count(&branch), Rc::weak_count(&branch));//1, 1println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));//2, 0}println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());//Some(Node { value: 5, parent: RefCell { value: None }, children: RefCell { value: [Node { value: 3, parent: RefCell { value: None }, children: RefCell { value: [] } }] } })println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));//1, 0
}//Recursive Types
enum List {Cons(i32, Box<List>),//Box<List> is a pointer to another List. This has a known size.Nil,
}//define a smart pointer
struct MyBox<T>(T);//implement Deref trait for MyBox<T>
impl<T> MyBox<T> {fn new(x: T) -> MyBox<T> {MyBox(x)}
}impl<T> Deref for MyBox<T> {//implement Deref trait for MyBox<T>type Target = T;//associated type for the Deref trait to usefn deref(&self) -> &T {//return a reference to the value we want to access with the * operator&self.0}
}fn hello(name: &str) {println!("Hello, {}!", name);
}struct CustomSmartPointer {data: String,
}impl Drop for CustomSmartPointer {//implement Drop traitfn drop(&mut self) {//drop methodprintln!("Dropping CustomSmartPointer with data `{}`!", self.data);}
}enum RcList {RcCons(i32, Rc<RcList>),RcNil,
}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>
whereT: Messenger,
{pub fn new(messenger: &T, max: usize) -> LimitTracker<T> {LimitTracker {messenger,//messenger: 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: You are over your quota!");} else if percentage_of_max >= 0.9 {self.messenger.send("Urgent warning: You've used up over 90% of your quota!");} else if percentage_of_max >= 0.75 {self.messenger.send("Warning: You've used up over 75% of your quota!");}}
}#[derive(Debug)]
enum List2 {Cons2(Rc<RefCell<i32>>, Rc<List2>),Nil2,
}#[derive(Debug)]
enum List3 {Cons3(i32, RefCell<Rc<List3>>),Nil3,
}impl List3 {fn tail(&self) -> Option<&RefCell<Rc<List3>>> {match self {Cons3(_, item) => Some(item),Nil3 => None,}}}#[derive(Debug)]
struct Node {value: i32,parent: RefCell<Weak<Node>>,children: RefCell<Vec<Rc<Node>>>,}#[cfg(test)]
mod tests {use super::*;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) {self.sent_messages.borrow_mut().push(String::from(message));//borrow_mut returns a RefMut<T> smart pointer//Every time we call borrow_mut, the mutable borrow counter goes up by one.//When a RefMut<T> value goes out of scope, the mutable borrow counter goes down by one.}}#[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);//borrow returns a Ref<T> smart pointer//Every time we call borrow, the immutable borrow counter goes up by one.//When a Ref<T> value goes out of scope, the immutable borrow counter goes down by one.}
}