Deref

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<T> {
  child: T,
}

struct DerefChild<T> {
  value: T,
}

impl<T> Deref for DerefParent<T> {
  type Target = T;

  fn deref(&self) -> &T {
    &self.child
  }
}

impl<T> Deref for DerefChild<T> {
  type Target = T;

  fn deref(&self) -> &T {
    &self.value
  }
}

以下のように使ってみて変数に割り当てます。

let deref_parent: DerefParent<DerefChild<String>> = DerefParent {
  child: DerefChild {
    value: "aiueo".to_string(),
  },
};

この時deref_parentstr::repeatを呼び出せます。

assert_eq!(deref_parent.replace("u", "*"), "ai*eo");

これは以下のように型変換される為です。

  1. DerefParent<DerefChild<String>からDerefChild<String>
  2. DerefChild<String>からString
  3. Stringからstr

DerefMut

::deref後の値を更新できるかはこのトレイトも持っているかどうかで決まりいます。このトレイトはDerefをスーパートレイトを持ちます。

例えばBoxDerefMutを持つので、ミュータブルな変数束縛からも問題無く::derefできます。

let mut value: Box<i32> = Box::new(33);

*value += 1;

assert_eq!(*value, 34);