解引用是一个由两个部分组成的操作。第一个部分是获取数据的引用,第二个部分是访问引用后面的数据,这是通过解引用运算符
*
来完成的。
Deref
特性只完成了第一个部分,即获取引用并将其提供给我们。然后,解引用运算符
*
会跟随引用(内存地址),使我们能够访问该内存地址上的数据,这部分实际上是编译器内置的功能。
fn main()
{
let age: i32 = 90;
let r_age = &age;
println!("{}", *r_age);
}
现在如果我们没有一个引用,我们怎么能够使用解引用运算符
*
来访问数据呢?
fn main()
{
let age: i32 = 90;
println!("{}", *(&age));
}
现在我们可以为自己的类型实现
Deref
特质。
use std::ops::Deref;
fn main()
{
let d = OurOwnType(77);
println!("{}", *d);
println!("{}", *(d.deref()));
println!("{}", *(Deref::deref(&d)));
println!("{}", *(&d.0));
}
struct OurOwnType(i32);
impl std::ops::Deref for OurOwnType
{
type Target = i32;
fn deref(&self) -> &Self::Target
{
&self.0
}
}
在*d编译器的幕后,它调用了Deref特质中的deref方法,就像d.deref()一样,这会给我们一个引用,然后使用解引用运算符*进行解引用操作,让我们访问数据。在这里,Deref特质完成了第一部分,给我们提供了一个引用。
*x
并不是所谓的调用deref
。*x
解引用一个&
-ptr。Deref
允许一个不是引用的对象 假装 成为一个引用,即Box<T>
可以像&T
一样运作。请参见 https://github.com/rust-lang/rfcs/blob/master/text/0241-deref-conversions.md。 - VeedracAsRef
,可能会更容易理解。不幸的是,我们已经有一个叫做那个名字的特性了... - Lambda Fairy