Rustの長い長い型名


まえがき

この記事の目的は、非常に長い名前の型を生成して、コンパイラで遊ぶこと型推論やトレイト境界のありがたさを理解することです。
記念すべき初投稿。

導入

RustやC++は、型名が長くなりやすい性質を持っています。
C++でSTLを駆使してコードを書いていたときは、型名の長さ(と<<>>の多さ)が嫌になってしまったこともあります。
Rustでは、型推論(とそれを助けるトレイト境界)のおかげで、長い型名を見る機会は少ないと思います。しかし、型名は水面下で存在していて、顕在化すると下記のような惨劇を引き起こします。

※impl Traitが導入されて上記の問題は解決されたものの、std::any::Anyを使うには未だ真の名前が必要なはず。

本題

本題のコードはこちら。get_longnameはVecとスライスを受け取ってイテレータを返す関数です。それほど不自然ではないと思いますが……

fn get_longname(input :Vec<isize>, n :&[usize])->impl Iterator<Item=isize>{
    let a = input.into_iter();
    let mut n = n.into_iter();

    let a = match n.next() {
        Some(&x) => Enum::A(a.take(x)),
              _  => Enum::B(a),
    };
    let a = match n.next() {
        Some(&x) => Enum::A(a.take(x)),
              _  => Enum::B(a),
    };
    let a = match n.next() {
        Some(&x) => Enum::A(a.take(x)),
              _  => Enum::B(a),
    };
    let a = match n.next() {
        Some(&x) => Enum::A(a.take(x)),
              _  => Enum::B(a),
    };
    a

}

戻り値の正確な型名はこちら
Enum<std::iter::Take<Enum<std::iter::Take<Enum<std::iter::Take<Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>, Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>>, Enum<std::iter::Take<Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>, Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>>>, Enum<std::iter::Take<Enum<std::iter::Take<Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>, Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>>, Enum<std::iter::Take<Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>, Enum<std::iter::Take<std::vec::IntoIter<isize>>, std::vec::IntoIter<isize>>>>>
ここまで長くなるなんて想像できましたか?
しかも、let a = match…から…};までの4行を追加するたびに型名の長さは指数関数的に増えます。どんな長さの型名も簡単に用意できますね。
Rustでは、このように型名が非常に長くなることがあります。時には扱っている型の真の名前を見てみるのも良いでしょう。変数の後ろに:u8と書けば確認できます(impl Trait以外)。

※Enumの定義はこちらのリンクのコードを使用いたしました。

enum Enum<A, B> {
    A(A),
    B(B),
}

impl<A, B> Iterator for Enum<A, B>
where
    A: Iterator,
    B: Iterator<Item = A::Item>,
{
    type Item = A::Item;
    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Enum::A(x) => x.next(),
            Enum::B(x) => x.next(),
        }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self {
            Enum::A(x) => x.size_hint(),
            Enum::B(x) => x.size_hint(),
        }
    }
}

まとめ

  • Rustの型名は長いよ。
  • でも普段は意識しないよ。
  • たまに確認しよう。

おまけ

matchを含む文を6個に増やした場合