Skip to content

Latest commit

 

History

History
157 lines (107 loc) · 3.79 KB

15.智能指针.md

File metadata and controls

157 lines (107 loc) · 3.79 KB

智能指针

Box 指针

指向堆上数据,并且可确定大小

应用场景:

  • 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
  • 当希望拥有一个值并只关心它的类型是否实现了特定 trait 而不是其具体类型的时候
  • 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候

Deref trait

实现该接口,可以重载解引用运算符

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

实现 Deref trait

use std::ops::Deref;


impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

解引用强制多态

定义函数

fn hello(name: &str) {
    println!("Hello, {}!", name);
}

传入 MyBox

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}

等价于

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&(*m)[..]);
}

更多实现
类似于如何使用 Deref trait 重载不可变引用的 * 运算符,Rust 提供了 DerefMut trait 用于重载可变引用的 * 运算符。

Rust 在发现类型和 trait 实现满足三种情况时会进行解引用强制多态:

  • 当 T: Deref<Target=U> 时从 &T 到 &U。 (不可变)
  • 当 T: DerefMut<Target=U> 时从 &mut T 到 &mut U。(可变 -> 可变)
  • 当 T: Deref<Target=U> 时从 &mut T 到 &U。(可变 -> 不可变)

Drop trait

Box 自定义了 Drop 用来释放 box 所指向的堆空间。

使用示例:

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 提早丢弃值

fn main() {
    let c = CustomSmartPointer { data: String::from("some data") };
    println!("CustomSmartPointer created.");
    drop(c);
    println!("CustomSmartPointer dropped before the end of main.");
}

注意:

无需担心意外的清理掉仍在使用的值,这会造成编译器错误:所有权系统确保引用总是有效的,也会确保 drop 只会在值不再被使用时被调用一次。

使用 RC 共享数据

示例:

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)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a));
}

注意:

也可以调用 a.clone() 而不是 Rc::clone(&a),不过在这里 Rust 的习惯是使用 Rc::clone。Rc::clone 的实现并不像大部分类型的 clone 实现那样对所有数据进行深拷贝。Rc::clone 只会增加引用计数,这并不会花费多少时间。深拷贝可能会花费很长时间。通过使用 Rc::clone 进行引用计数,可以明显的区别深拷贝类的克隆和增加引用计数类的克隆。

RefCell

通过 RefCell 在运行时检查借用规则.

应用场景:

  • Rc 允许相同数据有多个所有者;Box 和 RefCell 有单一所有者。
  • Box 允许在编译时执行不可变或可变借用检查;Rc仅允许在编译时执行不可变借用检查;RefCell 允许在运行时执行不可变或可变借用检查。
  • 因为 RefCell 允许在运行时执行可变借用检查,所以我们可以在即便 RefCell 自身是不可变的情况下修改其内部的值。

其中在不可变值内部改变值就是 内部可变性 模式。