Appearance
16.3 自动解引用的用处
用 Rc 这个“智能指针”举例。Rc 实现了 Deref:
rust
impl<T: ?Sized> Deref for Rc<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
&self.inner().value
}
}
它的 Target 类型是它的泛型参数 T。这么设计有什么好处呢?我们看下面的用法:
rust
use std::rc::Rc;
fn main() {
let s = Rc::new(String::from("hello"));
println!("{:?}", s.bytes());
}
我们创建了一个指向 String 类型的 Rc 指针,并调用了bytes()
方法。这里是不是有点奇怪?
这里的机制是这样的:Rc 类型本身并没有bytes()
方法,所以编译器会尝试自动 deref,试试s.deref().bytes()
。
String 类型其实也没有bytes()
方法,但是 String 可以继续 deref,于是再试试s.deref().deref().bytes()
。
这次在 str 类型中找到了bytes()
方法,于是编译通过。
我们实际上通过 Rc 类型的变量调用了 str 类型的方法,让这个智能指针透明。这就是自动 Deref 的意义。
实际上以下写法在编译器看起来是一样的:
rust
use std::rc::Rc;
use std::ops::Deref;
fn main() {
let s = Rc::new(String::from("hello"));
println!("length: {}", s.len());
println!("length: {}", s.deref().len());
println!("length: {}", s.deref().deref().len());
println!("length: {}", (*s).len());
println!("length: {}", (&*s).len());
println!("length: {}", (&**s).len());
}
这就是为什么 String 需要实现 Deref trait,是为了让&String 类型的变量可以在必要的时候自动转换为&str
类型。所以 String 类型的变量可以直接调用 str 类型的方法。比如:
rust
let s = String::from("hello");
let len = s.bytes();
虽然 s 的类型是 String,但它在调用bytes()
方法的时候,编译器会自动查找并转换为s.deref().bytes()
调用。所以 String 类型的变量就可以直接调用 str 类型的方法了。
同理:Vec<T>
类型也实现了 Deref trait,目标类型是[T]
,&Vec<T>
类型的变量就可以在必要的时候自动转换为&[T]
数组切片类型;Rc<T>
类型也实现了 Deref trait,目标类型是T
,Rc<T>
类型的变量就可以直接调用 T 类型的方法。
注意:&*两个操作符连写跟分开写是不同的含义。以下两种写法是不同的:
rust
fn joint() {
let s = Box::new(String::new());
let p = &*s;
println!("{} {}", p, s);
}
fn separate() {
let s = Box::new(String::new());
let tmp = *s;
let p = &tmp;
println!("{} {}", p, s);
}
fn main() {
joint();
separate();
}
fn joint()
是可以直接编译通过的,而fn separate()
是不能编译通过的。因为编译器很聪明,它看到&*
这两个操作连在一起的时候,会直接把&*s
表达式理解为s.deref()
,这时候 p 只是 s 的一个借用而已。而如果把这两个操作分开写,会先执行*s
把内部的数据 move 出来,再对这个临时变量取引用,这时候 s 已经被移走了,生命周期已经结束。
同样的,let p = &{*s};
这种写法也编译不过。这个花括号的存在创建了一个临时的代码块,在这个临时代码块内部先执行解引用,同样是 move 语义。
从这里我们也可以看到,默认的“取引用”、“解引用”操作是互补抵消的关系,互为逆运算。 但是,在 Rust 中,只允许自定义“解引用”,不允许自定义“取引用”。 如果类型有自定义“解引用”,那么对它执行“解引用”和“取引用”就不再是互补抵消的结果了。 先&
后*
以及先*
后&
的结果是不同的。