Rust unsize期待型とキャスト期待型
Rustの期待型は以下のようなデータ構造になっている。
#[derive(Copy, Clone, Debug)] pub enum Expectation<'tcx> { NoExpectation, ExpectHasType(Ty<'tcx>), ExpectCastableToType(Ty<'tcx>), ExpectRvalueLikeUnsized(Ty<'tcx>), }
このように4つのコンストラクタを持つ Expectation
だが、最も大きな働きをしているのは NoExpectation
と ExpectHasType
であり、ほぼ Option<Ty>
と思って問題ない。
この記事ではまず、残りの2つの動作について把握する。これは ExpectHasType
よりも弱い期待をするもので、ごく限定された場面でのみ生成される。
unsize期待型
ExpectRvalueLikeUnsized(U)
は、その式の型が T: Unsize<U>
であるような T
であることを期待するものである。
この期待型は rvalue_hint
によってのみ生成される。この関数は、
[T]
,str
,Trait
型に対してはExpectRvalueLikeUnsized
- それ以外の型に対しては
ExpectHasType
を期待する。この処理は以下の部分で行われる。
- 関数呼び出し引数の推論された期待型を推論するとき。
- 関数呼び出し引数を型強制するとき。 (
[T]
,str
,Trait
に対しては型強制は実行されない) box x
式の期待型がBox<T>
だったとき。&x
/&mut x
式のx
が左辺値で、期待型が&'a T
/&'a mut T
だったとき
これらに共通するのは、これらが全て Sized
な式を期待しているという点である。 (&x
で x
が左辺値なら x
は !Sized
かもしれない)
ExpectRvalueLikeUnsized
は to_option
によって取り出される。これを調べると、 ExpectRvalueLikeUnsized
は以下の用途にしか使われていないことがわかる。
- 配列リテラルの要素に対して型強制を行う。 (
[a, b, c]: ExpectRvalueLikeUnsized([T])
ならa
はT
に型強制される)- もちろん、
ExpectRvalueLikeUnsized
以外の期待型についてもこれは行われる。
- もちろん、
例えば以下の例では、配列の要素に対して型強制が行われている。
fn main() { let x : Box<[*const i32]> = Box::new([&1]); }
キャスト期待型
ExpectCastableToType(T)
は、その式の型が x as T
で変換可能であることを期待するものである。
これは as
式の型検査に対してのみ生成される。逆に、これが利用されるのは以下の場面のみである。
前者については、以下の例を見るとわかる。
fn main() { println!("{}", 2000000000000 as i64); // 2000000000000 println!("{}", (1000000000000 + 1000000000000) as i64); // -1454759936 }
これは以前の記事で紹介した仕組みだけでは説明できない。以前の記事で紹介した仕組みにより、 1000000000000 + 1000000000000
はこの左辺・右辺と同じ型をもつことが仮定される。そのため上の 2000000000000
が i64
になるなら原則として 1000000000000
も i64
になるはずだが、そうなっていない。
これは、ここで 2000000000000
が i64
になる仕組みが、型推論ではなく、期待型により実現されているからである。ここで説明したように x as i64
は内側の x
に対して ExpectCastableToType(i64)
を生成する。これを受けた整数リテラルは、サフィックスを持たないリテラルの型を i64
とおく。そして ExpectCastableToType
は演算子の内側には伝搬しないため、 1000000000000
は i64
とはならず、したがってデフォルトの i32
になってしまう。
後者には以下のような奇妙な例がある。
fn main() { let x = [&1] as [*const i32; 1]; }
これはコードの形に反して、 [&i32; 1]
から [*const i32; 1]
へのキャストではなく、 &i32
から *const i32
への型強制が行われている。
また、 ExpectCastableToType
は if
式の内側に伝搬しない という特徴がある。理由はその部分のコメントにあるように、then節側で厳しすぎる推論をしてしまうことを防止するためである。