Deref coercion(自动解引用类型转换)精制总结

2024/6/5 rust
  • Deref Coercion 是两个独立概念的“捏合体”

(自动)解引用Auto Deref (隐式)类型转换Coercion Type 因为这两个独立概念经常能够被叠加使用(比如,成员方法中的self参数),所以有了Deref Coercion的“合成概念”。

  • 语法功能:

实现【解引用】操作符*在自定义【智能指针】上的行为。从而,使【智能指针】如同【普通引用】一样使用。

  • 实质能力:

将A类型的实例转换成B类型实例的引用,只要A与B类型之间满足A: Deref<Target = B>或A: DerefMut<Target = B>。更具体的描述,请见下表:

img.png

  • 自动触发场景:

成员方法调用 / 字段访问 --- 对应于【(自动)解引用Auto Deref】

use std::fmt::{Display, Formatter, Result};
use std::ops::{Deref, DerefMut};
/*
   1. 破防【孤儿原则】的关键,本地包装器类型
   2. 将第三方`type`作为本地包装器类型的私有字段。
 */
struct Wrapper(Vec<String>);
/*
   3. 将第三方`trait`实现于本地包装器类型上
   4. 形成类型链条:第三方`trait` -被实现于-> 本地`Wrapper`类型 -代理-> 第三方`type`
 */
impl Display for Wrapper {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}
/*
   5. 给`Wrapper`实现`Deref / DerefMut trait`,将其变形为【智能指针】,
      从而达成【代理】第三方`type`的目的。
 */
impl Deref for Wrapper {
    type Target = Vec<String>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl DerefMut for Wrapper {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
fn main() {
    // 6. 最后,【孤儿原则】破防
    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("w = {}", w);
    println!("length = {}", w.len());
}

在A类型实例上用.操作符调用B类型实例上的成员方法·或·访问B类型实例上的字段值。 newtype设计模式利用这个技术点实现从外层包装类(即,智能指针)直接调用内部私有数据类型(被指向数据)的成员方法。 函数调用 [例程2] --- 对应于【强制类型(转换)Coercion Type】

use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}
fn hello(name: &str) {
    println!("Hello, {}!", name);
}

fn main() {
    /* 
        自动去引用`Deref coercion`包括了两步:
        (1) `&MyBox<String>` -> `&String`这是由`MyBox`类型的`Deref trait`实现提供的。
        (2) `&String` -> `&str`这是由`String`类型的`Deref trait`实现提供的。
        其中,`&MyBox<String>`与`&String`类型中的`&`引用操作符是触发`Deref coercion`的关键契机。
     */
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}

img.png

需要注意的只有一点:函数的实参必须是【智能指针】的【引用】(而不是【智能指针】自身)才可触发Deref coercion。

解引用操作。即,*A = B ,因为这实际执行了两步操作:

use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}
fn hello(name: i32) {
    println!("Hello, {}!", name);
}
fn main() {
    /* 
        去引用操作包括了两步:
        (1) `MyBox<i32>` -> `&i32`,自动去引用`Deref coercion`操作是
            由`MyBox`类型的`Deref trait`实现达成的。
        (2) `&i32` -> `i32`,去引用操作是由去引用操作符`*`完成的。
        即,去引用操作符`*`优先触发了自动去引用`Deref coercion`处理;
        然后,才对`Deref coercion`的处理结果,再做普通的去引用处理。
     */
    let m = MyBox::new(32);
    hello(*m);
}

先对A类型实例完成Deref::deref(&A)处理和返回&B(或DerefMut::deref_mut(&mut A)返回&mut B) 再对B类型实例的引用&B执行解引用操作*,得到B实例

  • 触发次数

若A与B都满足Deref / DerefMut trait限定条件,那么A -> &B的【解引用-类型转换】将被递归地连续执行,直至如下三个条件之一被达成,而结束递归: &B满足·函数签名中形参的类型要求 --- 函数调用场景 在&B上·找到了·被调用的成员方法 --- 成员方法调用场景 B未实现Deref / DerefMut trait,不再具备继续递归的条件。

  • 执行时间点

编译时,而不是运行时 --- 这又是一个零成本抽象。

转载原文链接:https://rustcc.cn/article?id=6eb47bdd-e4ce-477a-b6c6-70cdc044a6cb