问题的历史
在 Rust 中,类型通常封装其他值(例如,Box,Rc),开发者希望能够透明地访问嵌套值,就像在 C++ 中使用指针一样。为此引入了 trait Deref(和 DerefMut)以及用于方便访问的方法和运算符的自动解引用逻辑。
问题
在何时及如何自动解引用指针和智能指针之间的混淆,导致了编译错误,方法的意外行为,有时甚至因为多余的拷贝或借用而导致不优化的代码。
解决方案
Trait Deref 允许描述自己的解引用规则(例如,Box<T> 通过实现 Deref 行为像 T)。自动解引用系统在调用方法和运算符时尝试"解引用"对 Deref 类型的引用。这使得可以编写:
use std::rc::Rc; fn print_len(s: &str) { println!("{}", s.len()); } let s: Rc<String> = Rc::new("hello".into()); print_len(&s); // auto-deref: &Rc<String> -> &String -> &str
关键特性:
* 时工作,也在引用类型的匹配中自动解引用是否总是发生,当开发者"期望"时?
不:deref coercion 仅在调用方法时或需要一个在签名中通过 Deref Target 指定的其他类型的引用时发生。它并不适用于所有表达式,例如在模式匹配时。
deref coercion 会打破借用和所有权的规则吗?
不会。Deref coercion 完全遵守借用规则——不可能通过 Deref 获取两个可变引用,从而违反 Rust 的所有权安全。
如果 T: Deref<Target=U> 且两个类型未显式关联,&T 到 &U 的 auto-deref 可能吗?
是的,当调用一个期望 &U 的函数时,传入 &T,其中 T 实现了 Deref<Target=U>,将导致自动转换。
代码示例:
struct Wrapper(String); impl std::ops::Deref for Wrapper { type Target = String; fn deref(&self) -> &Self::Target { &self.0 } } fn takes_str(s: &str) {} let w = Wrapper("mytext".into()); takes_str(&w); // auto-deref: &Wrapper -> &String -> &str
开发者实现了自己的智能指针,但没有添加 Deref 实现,期待其像普通值一样工作。
优点:
类型能够编译,可以明确地访问嵌套值。
缺点:
在调用方法时 auto-deref 停止工作,使用标准库和第三方库的函数时不方便。
自己的包装结构实现了 Deref 和 DerefMut ,与 Box 类似,完美集成所有标准函数。
优点:
方便,绝大多数语言功能的透明工作,干净且与其他代码的良好集成。
缺点:
如果目标类型不明确,可能会增加 Deref 接口的复杂性。