Deref coercion(自动解引用类型转换)精制总结
- Deref Coercion 是两个独立概念的“捏合体”
(自动)解引用Auto Deref (隐式)类型转换Coercion Type 因为这两个独立概念经常能够被叠加使用(比如,成员方法中的self参数),所以有了Deref Coercion的“合成概念”。
- 语法功能:
实现【解引用】操作符*在自定义【智能指针】上的行为。从而,使【智能指针】如同【普通引用】一样使用。
- 实质能力:
将A类型的实例转换成B类型实例的引用,只要A与B类型之间满足A: Deref<Target = B>或A: DerefMut<Target = B>。更具体的描述,请见下表:
- 自动触发场景:
成员方法调用 / 字段访问 --- 对应于【(自动)解引用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);
}
需要注意的只有一点:函数的实参必须是【智能指针】的【引用】(而不是【智能指针】自身)才可触发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