Rust-智能指针:通过Deref trait 将智能指针当作常规引用处理

实现 Deref trait 允许我们重载 解引用运算符 (dereference operator) * (与乘法运算符或通配符相区别)。通过这种方法实现Deref trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。

通过解引用运算符追踪指针的值

常规引和是一个指针类型,一种理解指针的方式是将其看成指向储存在其他某处值的箭头。在以下例子中,创建了一个i32值的引用,接着使用解引用运算符来跟踪所引用的数据: 

    let x = 5;
    let y = &x;
    assert_eq!(5,x);
    assert_eq!(5,*y);

 变量x存放了一个i32值5。y等于x的一个引用。可以断言x等于5.。然而,如果希望对y的值做出断言,必须使用 *y 来追踪引用所指向的值(也就是 解引用)。一旦解引用了y,就可以访问y所指向的整型值并可以与5做比较。

相反如果尝试编写 assert_eq!(5,y),则会得到如下编译错误:

error[E0277]: can't compare `{integer}` with `&{integer}`
  --> src/main.rs:31:5
   |
31 |     assert_eq!(5,y);
   |     ^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}`
   |
   = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}`
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

不允许比较数字的引用与数字,因为它们是不同的类型。必须使用解引用运算符追踪引用所指向的值。

像引用一样使用Box<T>

可以使用Box<T>代替引用来重写以上的示例代码,解引用运算符也一样能工作,如下所示:

 

    let x = 5;
    let y = Box::new(x);
    assert_eq!(5,x);
    assert_eq!(5,*y);

 

在Box<i32>上使用解引用运算符。

自定义智能指针

为了体会默认情况下智能指针与引用的不同,让我们创建一个类似于标准库提供的Box<T>类型的智能指针。

从根本上说,Box<T>被定义为包含一个元素的元组结构体,所以在以下的一个示例中以相同的方式定义了MyBox<T>类型。我们还定义了new函数来对应定义于Box<T>的new函数:

struct MyBox<T>(T);

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

这里定义了一个结构体MyBox并声明了一个泛型参数T,因为我们希望其可以存放任何类型的值。MyBox是一个包含T类型元素的元组结构体。

MyBox::new函数获取一个T类型的参数并返回一个存放传入值的MyBox实例。我们修改下之前的代码,使用MyBox<T>类型代替Box<T>,示例代码不能编译,因为Rust不知道如何解引用MyBox:

    let x = 5;
    let y = MyBox::new(x);
    assert_eq!(5, x);
    assert_eq!(5, *y);

得到的编译错误是:

error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
  --> src/main.rs:32:19
   |
32 |     assert_eq!(5, *y);
   |                   ^^

MyBox<T>类型不能解引用,因为我们尚没在该类型实现这个功能。为了启用 * 运算符的解引用功能,需要实现 Deref trait。

通过实现Deref trait 将某类型像引用一样处理 

为了实现trait,需要提供trait所需的方法实现。Deref trait,由标准库提供,要求实现名为deref的方法,其借用self并返回一个内部数据的引用。MyBox<T>上的Deref实现:

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

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

type Targe = T; 语法定义了用于此trait的关联类型。关联类型是一个稍有不同的定义泛型参数的方式。

deref方法体中写入了 &self.0, 这样deref返回了我希望通过 * 运算符访问的值的引用。

深入学习

posted @ 2021-09-29 00:06  johnny_zhao  阅读(211)  评论(0编辑  收藏  举报