Deref
是 deref
操作符 *
的 trait,比如 *v
。
一般理解,*v
操作,是 &v
的反向操作,即試圖由資源的引用獲取到資源的拷貝(如果資源類型實現(xiàn)了 Copy
),或所有權(quán)(資源類型沒有實現(xiàn) Copy
)。
Rust 中,本操作符行為可以重載。這也是 Rust 操作符的基本特點。本身沒有什么特別的。
Deref
神奇的地方并不在本身 解引
這個意義上,Rust 的設計者在它之上附加了一個特性:強制隱式轉(zhuǎn)換
,這才是它神奇之處。
這種隱式轉(zhuǎn)換的規(guī)則為:
一個類型為 T
的對象 foo
,如果 T: Deref<Target=U>
,那么,相關(guān) foo
的某個智能指針或引用(比如 &foo
)在應用的時候會自動轉(zhuǎn)換成 &U
。
粗看這條規(guī)則,貌似有點類似于 AsRef
,而跟 解引
似乎風馬牛不相及。實際里面有些玄妙之處。
Rust 編譯器會在做 *v
操作的時候,自動先把 v
做引用歸一化操作,即轉(zhuǎn)換成內(nèi)部通用引用的形式 &v
,整個表達式就變成 *&v
。這里面有兩種情況:
Box
, Rc
, Arc
, Cow
等),轉(zhuǎn)成內(nèi)部標準形式 &v
;&
(比如:&&&&&&&v
),簡化成 &v
(通過插入足夠數(shù)量的 *
進行解引)。所以,它實際上在解引用之前做了一個引用的歸一化操作。
為什么要轉(zhuǎn)呢? 因為編譯器設計的能力是,只能夠?qū)?&v
這種引用進行解引用。其它形式的它不認識,所以要做引用歸一化操作。
使用引用進行過渡也是為了能夠防止不必要的拷貝。
下面舉一些例子:
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);
因為 String
實現(xiàn)了 Deref<Target=str>
。
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);
因為 Rc<T>
實現(xiàn)了 Deref<Target=T>
。
fn foo(s: &[i32]) {
// borrow a slice for a second
}
// Vec<T> implements Deref<Target=[T]>
let owned = vec![1, 2, 3];
foo(&owned);
因為 Vec<T>
實現(xiàn)了 Deref<Target=[T]>
。
struct Foo;
impl Foo {
fn foo(&self) { println!("Foo"); }
}
let f = &&Foo;
f.foo();
(&f).foo();
(&&f).foo();
(&&&&&&&&f).foo();
上面那幾種函數(shù)的調(diào)用,效果是一樣的。
coercion
的設計,是 Rust 中僅有的類型隱式轉(zhuǎn)換,設計它的目的,是為了簡化程序的書寫,讓代碼不至于過于繁瑣。把人從無盡的類型細節(jié)中解脫出來,讓書寫 Rust 代碼變成一件快樂的事情。