LoginSignup
25
23

More than 5 years have passed since last update.

Vec<T>::iter()はどこにあるの?

Last updated at Posted at 2015-02-24

この記事は執筆時点での情報を元にしています。 たぶん近い将来には公式ドキュメントが改良されてこの情報は必要なくなります。

こんにちは。 やる気は感じられるけどいつまで経っても仕様がstableにならないことで知られていたRust言語ですが、この度ついに、ついに1.0のリリーススケジュールが決まり、とりあえず触ってみるなら今かな? という時期が到来しています。
しかし残念ながらこの言語、くっそ難しい言語です。 ダイクストラ法ぐらいの規模のアルゴリズムを書くだけでも、意味不明なリファレンスと意味不明なコンパイルエラーに悩まされまくること請け合いです。 がんばってください。
皆さんに私と同じ苦しみを味わせたいのは山々ですが、ここでは涙を呑んで私が最近触ってハマったところをQiitaに書いていきたいと思います。

Vec<T>::iter()の定義ってどこに書いてあるの?

Vec<T>::iter()だけに限らず、特にVec<T>型とString型には、(2015年2月末現在)リファレンスにぱっと見で載ってないように見えるメソッドが多数存在します。 その中でも特に代表的なものとしてこのVec<T>::iter()を挙げました。 なんで特にこいつかというと、Vec型のリファレンスページのサンプルコード中で普通に呼んでるにもかかわらずそのリファレンスページ中のどこにも説明がないという、ちょっとアホかと言いたくなるような状態になっているからです。

順を追ってこのiter()メソッドがどこにあって、どうやって呼ばれるのかを説明していきます。

参照型のメソッド呼び出し時の暗黙の参照外し

まず思い出してほしいことその1です。
Rust型では参照型(&Hoge)も普通の型(Hoge)も、どちらも同じようにメソッドを呼び出し可能です。

let val : Option<i32> = Some(10);
println!("{}", val.unwrap());  // もちろん.unwrap()の呼び出しは合法

let refval : &Option<i32> = &val;
println!("{}", refval.unwrap());  // 参照型に対しても同じように.unwrap()を呼び出せる
println!("{}", (*refval).unwrap());  // 参照外しを明示的にやってもよい

Rustのコンパイラは、この動作を実現するために、a.b()という構文を見かけたら、

  1. aの型の impl の中に、bという名前のメソッドがあるか調べ、あったらそれを呼び出す。
  2. 1. に失敗したら、*aという参照外しの式が合法かを調べる。 aの型が参照ではないただの型だった(例えばai32型だった)場合は、コンパイルエラー。
  3. *aの型の impl の中に、bという名前のメソッドがあるか調べ、あったらそれを呼び出す。
  4. 3. に失敗したら、今度は**aという参照外しの式が合法かを…(2.に戻る)

という感じの処理を(たぶん)やっています。

演算子オーバーロードがあります

RustはJavaではないので、演算子オーバーロードがあります。
std::opsモジュール内にある、AddBitAnd traits を impl することにより、+演算子や&演算子をオーバーロードすることができるようになっています。 もちろん、参照外しの*演算子も、DerefまたはDerefMut traits を impl することで実現できてしまいます。
Deref trait のリファレンスページには、traitの定義がこう載ってます。

pub trait Deref {
    type Target;

    fn deref(&'a self) -> &'a <Self as Deref>::Target;
}

いろいろすっ飛ばして説明すると、Deref<Target=T> trait を impl する型は、参照外し演算子*&T型に変換できるということです。

犯人探し

ここまで分かった上でもう一度よくリファレンスページを見てみると、犯人が見つかります。

deref.png

お前か!
この impl のおかげで、Vec<T>型の変数に参照外しをかけると、&[T]型に変身するのです。 のみならず、
Vec<T>型に impl されていないメソッドを呼びだそうとすると、[T]型の impl にその名前のメソッドがないかを確認しに行くようになるのです。ちょっとした継承(どちらかというと委譲?)みたいなものですね。
果たして[T]型(slice型)のリファレンスページを見に行くと……ありました、iter()メソッド。
ということで、型のメソッドを調べたい時は、その型のリファレンスページだけでなくDerefで変換される型のリファレンスページも見ないとダメだよ!というのが今回の記事の結論になります。 お疲れさまでした。
近い将来、このわかりづらい仕様はドキュメントの方をなんとかする方向で調整が進んでいるようなので(でも1.0には間に合わなさそう……)、修正に期待しましょう。

参考: rustdoc: Include (or link to) methods "inherited" through auto-deref

25
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
23