2023学习日志

2023-07-27 21:29:01 浏览数 (1)

rust

智能指针

Box<T>指针

  • 堆上存储数据,而指针本身位于栈上 Box<T>类型的智能指针创建的是指向堆数据的指针,初始化过程中,会将数据分配在堆上,能够有效地节省栈上有限的空间,在所有权转移的过程中,复制的仅仅是一个栈上的指针,而非实际的数据,能够提高程序的性能 示例:
代码语言:rust复制
    fn main() {
        let b = Box::new(5);
        println!("b = {}",b);
    }
  • 允许创建递归类型 对于常见的cons list类型的数据结构(嵌套的列表),如果直接在声明结构体时进行嵌套,rust的编译器无法推断出该类型数据占用的内存大小,会在编译时报错。 示例:
代码语言:rust复制
//编译时将报错,无法推断出List结构体的具体大小
    use crate::List::{Cons, Nil};
    enum List{
        Cons(i32,List),
        Nil,
    }
    fn main(){
        let list = Cons(1, Cons(2, Cons(3, Nil)));
    }

此时可以使用Box<T>指针指向嵌套的列表,得到cons list类型的结构体。(指针的内存大小是已知的,但列表的大小是在进行结构体声明时未知的)

示例:

代码语言:rust复制
// 此段代码不会报错,因为Box<T>为智能指针,大小固定,编译器可以推断出List类型的大小
    use crate::List::{Cons, Nil};
    enum List{
        Cons(i32, Box<List>),
        Nil
    }
    fn main(){
        let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    }
  • 超出作用域之后,自动释放堆上数据 在超出作用域后,栈上的数据都会被逐一清除,而Box<T>智能指针在清除之前会调用其实现了的Drop trait的drop函数,清除所指向的堆上的数据。

Deref Trait 重载解引用运算符

智能指针类型的变量本身为指针类型,在使用时需要进行解引用来得到其所指向的数据。而解引用需要重载解引用运算符&。而通过实现Deref Trait,能够将类型像引用一样处理。

Deref trait需要实现其定义的deref方法,返回一个内部数据的引用

示例:

代码语言:rust复制
    //为Mybox<T>类型实现Deref trait
    use std::ops::Deref;
    impl<T> Deref for MyBox<T> {
        type Target = T;
        fn deref(&self) -> &self::Target {
            &self.0
        }
    }
    struct MyBox<T>(T);
    impl<T> MyBox<T> {
        fn new(x: T) -> MyBox<T> {
            MyBox(x)
        }
    }
    fn main() {
        let x=5;
        let y = MyBox::new(x);
        assert_eq!(5, x);
        assert_eq!(5, *y);
    }

Drop Trait 自动清除数据

Drop trait 类似于c 中的析构函数,在变量离开作用域时调用,清除数据或进行一些其他操作。

实现Drop trait需要实现其drop方法,该方法在变量离开时被调用,完成指定的一些操作。(主要目的为清理该变量拥有的数据)

此外,还可通过std::mem::drop 来在作用域结束前释放变量,std::mem::drop位于prelude中,因此无需显式引入该方法

示例:

代码语言:rust复制
    //为CustomSmartPointer结构体实现Drop trait
    struct CustomSmartPointer {
        data: String,
    }
    impl Drop for CustomSmartPointer {
        fn drop (&mut self){
            println!("Droping CustomSmartPointer with Data `{}`", self.data);
        
        }
    }
    fn main() {
        let c = CustomSmartPointer{
            data: String::from("my stuff"),
        };
        drop(c);
        let d = CustomSmartPointer{
            data: String::from("other stuff"),
        };
        println!("CustomSmartPointer created.");
    }

Rc<T>指针

Rc<T> 用于当我们希望在堆上分配一些数据供程序的多个部分读取,且无法在编译时确定程序的哪一部分会最终结束使用它的时候,如果确实知道哪部分是最后结束使用的话,可以令其成为数据的所有者,正常的所有权规则在编译时生效。

可以通过克隆Rc<T>的方式获取对堆上数据的引用,每次克隆时,引用计数增加1,当一个Rc<T>指针离开作用域时,引用计数减1,而当引用计数为0时,对应的drop方法将会被调用,堆上数据将会被清理。

注意:Rc<T>克隆的结果为不可变引用rust不允许同时存在多个可变引用

  • 强引用 --Rc<T> Rc<T>指针强引用,可以通过调用Rc::clone方法返回一个Rc<T>指针,会导致引用计数发生变化,当引用计数为0时,指针所指向的堆上数据将会被清理
  • 弱引用 --Weak<T> Weak<T>指针弱引用,可以通过Rc::downgrade方法返回一个*Weak<T>指针,不会导致引用计数发生变化,不会对堆上数据的清理产生影响 因为Weak<T>引用的值可能已经被丢弃了,因此需要在使用Weak<T>所指向的值时,调用Weak<T>实例的upgrade方法,返回一个Option<Rc<T>>

示例:

代码语言:rust复制
    use crate::List::{Cons, Nil};
    use std::rc::Rc;
    enum List {
        Cons(i32, Rc<List>),
        Nil,
    }
    fn main(){
        let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
        println!("count after creating a = {}", Rc::strong_count(&a));
        //Rc::clone得到的是Rc<T>类型的强引用,会影响引用计数
        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));
    }

RefCell<T>指针

  • 内部可变性 内部可变性rust中的一个设计模式,它允许你即使在有不可变引用时也可以改变数据,这通常是借用规则不允许的。该模式使用unsafe代码来模糊rust可变性借用规则。 当可以确保代码在运行时会遵守借用规则,即使是编译器无法保证的情况,可以选择使用运用了内部可变性模式的类型。
  • RefCell<T>运行时检查借用规则 RerCell<T>遵循内部可变性模式,在运行时检查借用规则而非编译时。 因为RefCell<T>允许在运行时检查借用规则,因此可以在RefCell自身不可变的情况修改其内部的值。 示例:
代码语言:rust复制
    use std::cell::RefCell;
    trait Messenger {
        fn send(&self, msg: &str);
    }
    struct MockMessenger {
        sent_messages: RefCell<Vec<String>>,
    }
    impl Messager for MockMessenger {
        fn send(&self, message: &str) {
            //borrow_mut()方法获取的是RefMut<T>类型的智能指针,borrow()方法获取的是Ref<T>类型的智能指针
            self.sent_message.borrow_mut().push(String::from(message));
        }
    }
  • RefCell<T>在运行时记录借用 borrow_mut方法获取的是RefMut<T>类型的智能指针,borrow方法获取的是Ref<T>类型的智能指针 RefCell<T>记录当前有多少个活动的Ref<T>RefMut<T>智能指针,每次调用borrow方法,RefCell<T>将不可变借用计数加一,当Ref<T>指针离开作用域时,不可变计数减一。可变借用计数规则类似不可解压计数规则。 与编译时借用规则相同:RefCell<T>在任何时刻只允许存在多个不可变借用一个可变借用。 示例:
代码语言:rust复制
    impl Messenger for MockMessenger {
        fn send(&self, message: &str){
            //代码将报错,不能同时存在两个可变借用
            let mut one_borrow = self.sent_message.borrow_mut();
            let mut two_borrow = self.sent_message.borrow_mut();
        }
    }

0 人点赞