主页 > 手机  > 

Rust8.1SmartPointers

Rust8.1SmartPointers
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 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. } }
标签:

Rust8.1SmartPointers由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Rust8.1SmartPointers