Rustにおける演算子の型推論の特殊ルール

概要: 原則として x + y::std::ops::Add(x, y) の構文糖衣であるが、型推論で特別扱いされる。

演算子の脱糖

演算子の脱糖は型推論の後、HIRからMIRへの変換のタイミングで行われる。原則として x + y::std::ops::Add(x, y) の構文糖衣である。これは *x/x[i] 以外の他の演算子についても同様である。しかし型推論では以下のような特殊扱いがある。

fn main() {
    use std::ops::Add;
    let x : i8 = 1 + 1; // OK
    let x : i8 = Add::add(1, 1); // Error
}

下側のコードがエラーになる理由

Rustの演算子オーバーロード可能であり、型の制約が少ない。具体的には「左辺と右辺の型から、計算結果の型が一意に決まる」ということだけが要請される。そのため型 A と型 B の値を足して型 C の値ができることがありえる。例えば、以下のような足し算を定義することができる。

fn main() {
    use std::ops::Add;
    struct A;
    struct B;
    impl Add<B> for A {
        type Output = i8;
        fn add(self, other: B) -> Self::Output { 42 }
    }
    let x : i8 = 1i8 + 1i8; // OK
    let x : i8 = A + B; // OK
}

つまり、外側の型 i8 から、内側の型を決めることは基本的にできない。演算子に限らず、戻り値型が <A as Add<B>>::Output のような射影型になっているときは、型はボトムアップにしか伝搬しない。

つまり、 Add::add(1, 1) : i8 から 1 : i8 は推論されない。この型情報の不足から、 1: {integer} だけが判明する。これは後でデフォルトである i32 と推論されるが、すると Add::add(1i32, 1i32) : i32 であり型が一致しない。

上側のコードがエラーにならない理由

lhs + rhs で、 lhs, rhs がある特定の型の場合に、Rustは「lhs, rhs, lhs + rhs の型は全て同じ」という制約を追加する。これにより自動的に内側の 1i8 であることが判明する。

演算子ごとの動作の違い

二項演算子は以下の4種類に分類される。それぞれ、特定の条件下型に対して追加の仮定をおく

  • 短絡回路演算子: ||, &&
    • 常に、左辺/右辺/戻り値は bool である。
  • シフト演算子: <<, >>
    • 両辺がともに整数型であるとき、左辺の型と戻り値の型は等しい。
  • 数学演算子: +, -, *, /, %
    • 両辺がともに整数型であるか、両辺がともに浮動小数点数型であるとき、左辺の型と右辺の型と戻り値の型は等しい。
  • ビット演算子: |, &, ^
    • 両辺がともに整数型であるか、両辺がともに浮動小数点数型であるか、両辺がともに bool であるとき、左辺の型と右辺の型と戻り値の型は等しい。
  • 比較演算子: ==, !=, <, <=, >, >=
    • 両辺がともにスカラー型であるとき、左辺の型と右辺の型は等しく、戻り値の型は bool に等しい。

ただし、スカラー型とは、 bool/char/整数型/浮動小数点数型/関数定義/関数ポインタ/生ポインタ のいずれかである。

「整数型である」「浮動小数点数型である」というのは、 i32usize のような特定の型のほかに、 {integer} のような推論型も含む。

単項演算子の場合

3つある単項演算子 */!/- のうち、 !-似たような動作をする。ただし以下の違いがある:

  • これらの演算子に遭遇したタイミングで structurally_resolved_type が呼ばれる。そのため、被演算子の型がこの時点で全く判明していない場合は、エラーになる。

! は、被演算子が整数または bool のとき、 - は、被演算子が整数または浮動小数点数のときに、組込み演算子とみなされる。このとき戻り値の型は被演算子の型と等しいと仮定される。

まとめ

  • x + y はほぼ ::std::ops::Add(x, y) の構文糖衣であるが、型推論の動作が微妙に異なる。
  • xy が特定の型のときは、型推論の時点で「組込み演算子である」と判定され、トップダウン型推論が可能になる。