Rustコトハジメ

プログラミング言語Rustに関する情報をお届けします。

メソッド呼び出しにおける参照解決アルゴリズムをざっくり理解する

Rustでは、様々な暗黙的型変換が行われる。

  • auto-referencing: メソッド呼び出しの&selfや&mut selfに対しては自動的に&や&mutをつけてくれる
  • auto-dereferencing: 引数に対して型が合うまで無限に*をつけてくれる

など、色々なことが出てきて、何をどう組み合わせて良いやらわからなくなる。

この記事では、メソッド呼び出し時の参照解決についての質問に対してstackoverflowで解説されている"The core of the algorithm is ..."についてざっくり理解する。

スレッドは以下

stackoverflow.com

f:id:akiradeveloper529:20190307141357j:plain

日本語で書いてみると、

  • foreach: derefを色々試す (U=T, U=*T, U=**T, ...)
    • auto-refなしでやってみる (U)
    • auto-refありでやってみる (&Uと&mut U)

ここで興味深いのは、dereferencingについてループするということだ。

impl ops::Deref for String {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

人間の頭には、例えばStringをレシーバにとって&strのメソッド(例えばtrimメソッド)が呼ばれるまでには、

  1. Stringが&Stringになる (auto-ref)
  2. &Stringから&strに変換される (deref)

という順になるが、アルゴリズムとしてはその逆で、

  1. U=Stringがだめで、次にU = *String = *deref(&String) = *&str = strとなった (*x on non-pointer types is equivalent to *Deref::deref(&x) std::ops::Deref - Rust)
  2. auto-refありなら&str型のレシーバが見つかった

という順で&strにマッチさせる。