美文网首页
Rust编程语言-15-智能指针

Rust编程语言-15-智能指针

作者: onemoremile | 来源:发表于2022-01-30 23:42 被阅读0次

    指针:是指一个包含了内存地址的变量,这个地址代表或者指向其它的数据,最常用的一种指针就是引用reference,用符号&表示,借用了指向的值

    智能指针:一种结构体,不止具备指针的能力,并且包含了额外的元数据metadata和能力,比如引用计数智能指针(追踪数据的多个所有者,如果所有者为0,则数据被销毁);

    冷知识:String和Vec<T>其实也是智能指针,理由是他们不光拥有一块内存允许我们操作它,而且也有元数据(比如容量)和额外的能力、保障(如String要求它的数据必须是有效的UTF-8字符)

    智能指针一般用结构体struct来实现,区别与一般的struct,它还会实现Dref和Drop这两个trait,Dref解引用允许智能指针具有引用的能力,Drop允许自定义销毁变量时候的行为

    Box<T>

    Box<T>让数据存储在堆heap上,而不是在栈stack上,同时在栈上会有一个指针指向堆中的内存地址数据

    看代码

    fn main() {
        let b = Box::new(5);
        println!("b = {}", b);
    }
    

    println语句执行完后,b会销毁,同时b指向的堆上的数据5也会被销毁

    如下代码无法编译通过:

    enum List {
        Cons(i32, List),
        Nil,
    }
    
    use crate::List::{Cons, Nil};
    
    fn main() {
        let list = Cons(1, Cons(2, Cons(3, Nil)));
    }
    

    报错如下:

    $ cargo run
       Compiling cons-list v0.1.0 (file:///projects/cons-list)
    error[E0072]: recursive type `List` has infinite size
     --> src/main.rs:1:1
      |
    1 | enum List {
      | ^^^^^^^^^ recursive type has infinite size
    2 |     Cons(i32, List),
      |               ---- recursive without indirection
      |
    help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable
      |
    2 |     Cons(i32, Box<List>),
      |               ^^^^    ^
    
    error[E0391]: cycle detected when computing drop-check constraints for `List`
     --> src/main.rs:1:1
      |
    1 | enum List {
      | ^^^^^^^^^
      |
      = note: ...which again requires computing drop-check constraints for `List`, completing the cycle
      = note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: List } }`
    
    Some errors have detailed explanations: E0072, E0391.
    For more information about an error, try `rustc --explain E0072`.
    error: could not compile `cons-list` due to 2 previous errors
    
    

    报错信息的意思是infinite size无穷大小,原因是其中含有递归recursive,Rust无法判断它占用的空间大小

    Rust如何计算非递归类型所占空间大小

    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }
    

    枚举类型的大小,Rust会选择占用空间最大的那个

    智能指针-实现了Dref trait的引用reference

    如下代码编译失败!can't compare {integer} with &{integer}

    fn main() {
        let x = 5;
        let y = &x;
    
        assert_eq!(5, x);
        assert_eq!(5, *y);
    }
    

    不可以把两个不同类型一起比较,一个是integer,一个是&integer,不具备可比性

    上述代码修改成:

    fn main() {
        let x = 5;
        let y = Box::new(x);
    
        assert_eq!(5, x);
        assert_eq!(5, *y);
    }
    

    使用Box<T>类型后,可以正常编译,测试通过

    自定义智能指针 MyBox<T>

    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);
    }
    

    此行代码assert_eq!(5, *y) 还是不能通过编译,原因是MyBox<T>没有实现Dref trait,导致解引用无法获取具体的值

    所以,需要新增Dref trait的实现

    impl<T> Deref for MyBox<T> {
        type Target = T;
    
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    

    *y背后的代码其实是 *(y.deref())

    函数或方法中的强制隐式解引用

    fn hello(name: &str) {
        println!("Hello, {}!", name);
    }
    
    fn main() {
        let m = MyBox::new(String::from("Rust"));
        hello(&m);
    }
    

    上面代码中,hello函数的参数是&str,是String的切片,由于MyBox<T>实现了Dref的trait,所以&m会解引用为String,而String又可以解引用为&str,所以可以编译通过

    如果Rust没有解引用胁迫机制,hello函数的调用要写成 hello(&(*m)[..]); 可读性非常差;Rust的解引用根据需要可以执行许多次,为了要找到合适的匹配参数的引用,这个动作是在编译器,所以对程序运行时没有影响

    可变性中的强制解引用

    • From &T to &U when T: Deref<Target=U>
    • From &mut T to &mut U when T: DerefMut<Target=U>
    • From &mut T to &U when T: Deref<Target=U>

    Drop trait

    struct CustomSmartPointer {
        data: String,
    }
    
    impl Drop for CustomSmartPointer {
        fn drop(&mut self) {
            println!("Dropping CustomSmartPointer with data `{}`!", self.data);
        }
    }
    
    fn main() {
        let c = CustomSmartPointer {
            data: String::from("my stuff"),
        };
        let d = CustomSmartPointer {
            data: String::from("other stuff"),
        };
        println!("CustomSmartPointers created.");
    }
    

    可以使用std::mem::drop来提前释放占用的资源

    RC<T>引用计数智能指针

    大部分场景下,一个变量对一个值拥有所有权,但是某些场景下,如图结构,某个节点是可以有多条边的,除非所有的边都释放了所有权,这个值才能被释放

    enum List {
        Cons(i32, Rc<List>),
        Nil,
    }
    
    use crate::List::{Cons, Nil};
    use std::rc::Rc;
    
    fn main() {
        let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
        println!("count after creating a = {}", Rc::strong_count(&a));
        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));
    }
    

    a变量,第一次定义和声明的时候,引用计数为1;b定义的时候,a的引用计数加1,变为2,c定义的时候,a的引用计数又增加1,变为3,当c在{}执行完后,由于c释放,导致a的引用计数减1,变为2,然后当main函数执行完毕后,a的引用变为0,a的资源得到释放

    RefCell<T> 内部可变

    强制在运行时应用借用规则,应用场景如mock object

    相关文章

      网友评论

          本文标题:Rust编程语言-15-智能指针

          本文链接:https://www.haomeiwen.com/subject/rtvyhrtx.html