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

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

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

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

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

Box

Box让数据存储在堆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),
  |               ^^^^    ^

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类型后,可以正常编译,测试通过

自定义智能指针 MyBox

struct MyBox(T);

impl MyBox {
    fn new(x: T) -> MyBox {
        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没有实现Dref trait,导致解引用无法获取具体的值

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

impl Deref for MyBox {
    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实现了Dref的trait,所以&m会解引用为String,而String又可以解引用为&str,所以可以编译通过

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

可变性中的强制解引用

  • From &T to &U when T: Deref
  • From &mut T to &mut U when T: DerefMut
  • From &mut T to &U when T: Deref

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引用计数智能指针

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

enum List {
    Cons(i32, Rc),
    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 内部可变

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

你可能感兴趣的:(Rust编程语言-15-智能指针)