$30 off During Our Annual Pro Sale. View Details »

Rust の LT 会! 2016/11/21

Rust の LT 会! 2016/11/21

Agata Naomichi

November 21, 2016
Tweet

More Decks by Agata Naomichi

Other Decks in Programming

Transcript

  1. ジェネリクスと
    静的ディスパッチと
    エラーメッセージ
    2016/11/21 RUST
    の LT
    会!

    View Slide

  2. ABOUT ME
    agatan
    Twitter:
    GitHub:
    趣味は言語処理系/
    コンパイラいじり
    自作言語の妄想をするのがすきです
    システムプログラミング系の研究室にいます
    @agatan_
    http://github.com/agatan/

    View Slide

  3. RUST
    のジェネリクス
    一つのコードで複数の型に対応する
    /
    trait
    制約を付けられる
    使うときは具体的な型をいれる
    /
    そして型推論
    fn hello(x: T) -> String {
    format!("Hello {}", x)
    }

    View Slide

  4. どうコンパイルされるか
    Rust
    のジェネリクスは monomorphization
    によってコ
    ンパイルされる
    インスタンス化されるたびに専用の関数をつくる
    fn snd(x: (T, U)) -> U { x.1 }
    let a: (i64, i64) = (0, 1);
    snd(a); // => 1
    let b: (Vec, Option) = (vec![], None);
    snd(b); // => None
    この例では と
    がインスタンス化されている
    let a: (i64, i64) = (0, 1);
    snd_i64_i64(a); // <= 専用 関数 呼
    let b: (Vec, Option) = (vec![], None);
    snd_vec_i32_option_u64(b); // <= 専用 関数 呼
    こんな感じにコンパイルされる

    View Slide

  5. ジェネリクスとトレイト
    trait Greet {
    fn greet(&self) -> String;
    }
    impl Greet for String {
    fn greet(&self) -> String { format!("Hello, I'm {}", self) }
    }
    impl Greet for i64 {
    fn greet(&self) -> String { format!("Hello, I'm No.{}", self) }
    }
    ジェネリクスにトレイトによる制約がつけられる
    fn conversation(t: T, u: U) {
    println!("T => {}", t.greet());
    println!("U => {}", u.greet());
    }
    monomorphization
    によってコンパイルされる
    の部分はどうなる?

    View Slide

  6. 静的ディスパッチ
    専用の関数はどうコン
    パイルされるか
    コンパイル時にどの関数を呼ぶかすべてわかっている
    =>
    静的ディスパッチ
    の っぽい
    fn conversation_i64_String(t: i64, u: String) {
    println!("T => {}", t.greet()); // => greet i64::greet
    println!("U => {}", u.greet()); // => greet String::greet
    }

    View Slide

  7. 静的ディスパッチ
    利点
    インライン展開できる!!
    関数を動的に探す必要がない!!(
    ただの関数呼び
    出し)
    欠点
    バイナリが大きくなる...
    分割コンパイルできない

    View Slide

  8. 動的ディスパッチ
    どの関数を呼ぶか実行時に決定する
    の仮想関数
    class A {
    public virtual void greet() { std::cout << "Hello, I'm A\n"; }
    }
    class B: public A {
    public virtual void greet() override { std::cout << "Hello, I'm B\n"
    }
    void greet(A const& a) {
    a.greet(); // A::greet? B::greet?
    }
    典型的には仮想関数のテーブルをオブジェクトに紐付
    けておく
    実行時に対象オブジェクトの に対応する関数
    ポインタを探す

    View Slide

  9. 動的ディスパッチ
    Rust
    ではトレイトオブジェクトを使う
    /
    =
    「 を満たす何か」のベ
    クタ
    とはかけない
    利点
    バイナリサイズの節約
    分割コンパイルを諦める必要がない
    欠点
    実行時コストがかかる

    View Slide

  10. 型が爆発する
    monomorphization
    を進めていくと,型が爆発する
    自前のちょっとしたパーサコンビネータの例
    let mut parser = char('"')
    .and(take_while(|x: &char| *x != '"'))
    .and(char('"'))
    .map(|((_, s), _)| s);
    の型は
    Map<
    And<
    And<
    Char>,
    TakeWhile<[closure@src/main.rs:253:25: 253:45], StrInput<'a>, String>
    >,
    Char>
    >,
    [closure@src/main.rs:255:14: 255:29]
    >

    View Slide

  11. EXPRESSION TEMPLATE
    式を型で表す!
    c.f. Boost.Spirit (C++
    のパーサコンビネータ)
    Rust
    には型推論があるので,型の複雑さはほとんど気
    にならない!
    関数定義のときは返り値の型を明示しなくてはならな
    くて若干つらい...
    この辺は読みやすさとのトレードオフ
    Rust
    のコンパイルエラーはわかりやすいので
    expression template
    も(そこまで)辛くない!!

    View Slide

  12. わざとエラーを起こしてみる
    としてコンパイルエラーを起こ
    してみると
    =>
    読みやすい!!
    良い感じに詳細が省かれた型名を表示してくれる
    どういう基準でやっているのかはよくわからない...

    View Slide

  13. もっとエラーを起こしてみる
    とやってみる
    までは合っている.3
    つ目の
    が間違っている.
    =>
    本当に読みやすい!!!!

    View Slide

  14. まとめ
    Rust
    のジェネリクスは静的に色々解決する
    動的にもできる (
    トレイトオブジェクト )
    ジェネリクスしまくると型が爆発する!!
    Rust
    の型推論は強力なので,ほとんど気にならない
    型が合わなくなってもエラーメッセージが本当にわか
    りやすい!!!
    expression template
    的なことをしてもそんなに辛く
    ない
    型以外にもエラーメッセージは本当に読みやすくて
    最高

    View Slide