この記事は執筆時点での情報を元にしています。 たぶん近い将来には公式ドキュメントが改良されてこの情報は必要なくなります。
こんにちは。 やる気は感じられるけどいつまで経っても仕様が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()
という構文を見かけたら、
-
a
の型の impl の中に、b
という名前のメソッドがあるか調べ、あったらそれを呼び出す。 -
- に失敗したら、
*a
という参照外しの式が合法かを調べる。
a
の型が参照ではないただの型だった(例えばa
がi32
型だった)場合は、コンパイルエラー。
- に失敗したら、
-
*a
の型の impl の中に、b
という名前のメソッドがあるか調べ、あったらそれを呼び出す。 -
- に失敗したら、今度は
**a
という参照外しの式が合法かを…(2.に戻る)
- に失敗したら、今度は
という感じの処理を(たぶん)やっています。
演算子オーバーロードがあります
RustはJavaではないので、演算子オーバーロードがあります。
std::ops
モジュール内にある、Add
やBitAnd
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
型に変換できるということです。
犯人探し
ここまで分かった上でもう一度よくリファレンスページを見てみると、犯人が見つかります。
お前か!
この 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