Rust学习笔记
Rust编程语言入门教程课程笔记
Lecture 15: Smart Pointers
src/main.rs
rust
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 Types
let _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 type
let 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 prelude
let c = CustomSmartPointer { data: String::from("my stuff") };
drop(c);//force a value to be dropped sooner than the end of its scope
let 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 scenarios
let list_a = Rc::new(RcCons(5,
Rc::new(RcCons(10,
Rc::new(RcNil)))));
println!("count after creating list_a = {}", Rc::strong_count(&list_a));//1
let 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 pointer
println!("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));//1
println!("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));//2
println!("b initial rc count = {}", Rc::strong_count(&b));//1
println!("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));//2
println!("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 References
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),//Weak::new() creates a Weak<T> that doesn't have an owner
children: RefCell::new(vec![]),
});
println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));//1, 0
println!("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 owner
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);//Rc::downgrade creates a Weak<T> from a Rc<T> reference
println!("branch strong = {}, weak = {}", Rc::strong_count(&branch), Rc::weak_count(&branch));//1, 1
println!("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 use
fn 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 trait
fn drop(&mut self) {//drop method
println!("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>
where
T: 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.
}
}