Deref
は Rust が提供する暗黙的な型変換を行う特殊なトレイトの1つです。このトレイトは::deref
の実装を要求し、これにより参照外し演算子*
を使った時に適当な戻り値になるように上書きできます。
参照外し演算子による型変換
Box<T>
はDeref
により*
を使った場合T
に型変換されるようになってます。
let box_str: Box<&str> = Box::new("str");
assert_eq!(*box_str.to_string(), "str".to_string());
参照による型変換
Deref<Target=T>
を実装するU
がある時、&U
が自動的に&T
に型強制されます。
これもBox<T>
はDeref<Target=T>
を実装済なので、参照化するだけでT
に型強制されます。
let box_str: Box<&str> = Box::new("str");
assert_eq!((&box_str).to_string(), "str".to_string());
メソッド呼び出しによる型変換
先程から&str
型に変換してから::to_string
としていますが、メソッド呼び出し時に自動で::deref
による型変換されるというルールがあるので、Box<str>
型のまま::to_string
などstr
型のメソッドを呼び出せます。
let box_str: Box<&str> = Box::new("str");
assert_eq!(box_str.to_string(), "str".to_string());
再帰的な型変換
Deref
は要件を満たせるまで再帰的::deref
で型を遡ります。例えばメソッド呼び出しの場合、そのメソッドを持つ型になるまで再帰的に型変換されます。
確認の為に、以下のような定義済の Struct や Trait があるスコープがあるとします。
まず、DerefParent<T>
はchild: T
を持ち、::deref
により単にそれを返します。次にDerefChild<T>
はvalue: T
を持ち、こちらも::deref
によりそれを返します。
use std::ops::Deref;
struct DerefParent {
child: T,
}
struct DerefChild {
value: T,
}
impl Deref for DerefParent {
type Target = T;
fn deref(&self) -> &T {
&self.child
}
}
impl Deref for DerefChild {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
以下のように使ってみて変数に割り当てます。
let deref_parent: DerefParent> = DerefParent {
child: DerefChild {
value: "aiueo".to_string(),
},
};
この時deref_parent
はstr
の::repeat
を呼び出せます。
assert_eq!(deref_parent.replace("u", "*"), "ai*eo");
これは以下のように型変換される為です。
DerefParent<DerefChild<String>
からDerefChild<String>
DerefChild<String>
からString
String
からstr
DerefMut
::deref
後の値を更新できるかはこのトレイトも持っているかどうかで決まりいます。このトレイトはDeref
をスーパートレイトを持ちます。
例えばBox
はDerefMut
を持つので、ミュータブルな変数束縛からも問題無く::deref
できます。
let mut value: Box = Box::new(33);
*value += 1;
assert_eq!(*value, 34);