Deref
标准库提供了一个特殊的特性,Deref。它一般用来重载*,解引用运算符:
*
use std::ops::Deref; struct DerefExample<T> { value: T, } impl<T> Deref for DerefExample<T> { type Target = T; fn deref(&self) -> &T { &self.value } } fn main() { let x = DerefExample { value: 'a' }; assert_eq!('a', *x); }
这对编写自定义指针类型很有用。然而,有一个与Deref相关的语言功能:“解引用强制多态(deref coercions)”。规则如下:如果你有一个U类型,和它的实现Deref<Target=T>,(那么)&U的值将会自动转换为&T。这是一个例子:
U
Deref<Target=T>
&U
&T
fn foo(s: &str) { // borrow a string for a second } // String implements Deref<Target=str> let owned = "Hello".to_string(); // therefore, this works: foo(&owned);
在一个值的前面用&号获取它的引用。所以owned是一个String,&owned是一个&String,而因为impl Deref<Target=str> for String,&String将会转换为&str,而它是foo()需要的。
&
owned
String
&owned
&String
impl Deref<Target=str> for String
&str
foo()
这就是了。这是Rust唯一一个为你进行一个自动转换的地方,不过它增加了很多灵活性。例如,Rc<T>类型实现了Deref<Target=T>,所以这可以工作:
Rc<T>
use std::rc::Rc; fn foo(s: &str) { // borrow a string for a second } // String implements Deref<Target=str> let owned = "Hello".to_string(); let counted = Rc::new(owned); // therefore, this works: foo(&counted);
我们所做的一切就是把我们的String封装到了一个Rc<T>里。不过现在我们可以传递Rc<String>给任何我们有一个String的地方。foo的签名并无变化,不过它对这两个类型都能正常工作。这个例子有两个转换:Rc<String>转换为String接着是String转换为&str。只要类型匹配Rust将可以做任意多次这样的转换。
Rc<String>
foo
标准库提供的另一个非常通用的实现是:
fn foo(s: &[i32]) { // borrow a slice for a second } // Vec<T> implements Deref<Target=[T]> let owned = vec![1, 2, 3]; foo(&owned);
向量可以Deref为一个切片。
当调用一个方法时Deref也会出现。考虑下面的例子:
struct Foo; impl Foo { fn foo(&self) { println!("Foo"); } } let f = &&Foo; f.foo();
即便f是&&Foo,而foo接受&self,这也是可以工作的。因为这些都是一样的:
f
&&Foo
&self
f.foo(); (&f).foo(); (&&f).foo(); (&&&&&&&&f).foo();
一个&&&&&&&&&&&&&&&&Foo类型的值仍然可以调用Foo定义的方法,因为编译器会插入足够多的*来使类型正确。而正因为它插入*,它用了Deref。
&&&&&&&&&&&&&&&&Foo
Foo
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8