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

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.
    }
}
相关推荐
黑叶白树1 小时前
简单的签到程序 python笔记
笔记·python
幸运超级加倍~1 小时前
软件设计师-上午题-15 计算机网络(5分)
笔记·计算机网络
老猿讲编程2 小时前
用示例来看C2Rust工具的使用和功能介绍
rust
金庆2 小时前
How to set_default() using config-rs crate
rust·config·set_default·valuekind
芊寻(嵌入式)3 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
准橙考典3 小时前
怎么能更好的通过驾考呢?
人工智能·笔记·自动驾驶·汽车·学习方法
许野平4 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
密码小丑5 小时前
11月4日(内网横向移动(一))
笔记
鸭鸭梨吖5 小时前
产品经理笔记
笔记·产品经理
齐 飞6 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb