注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
Rustは安全性に焦点を合わせており、異なる型の間を互いにキャストするために二つの異なる方法を提供しています。
一つは as
であり、これは安全なキャストに使われます。
逆に transmute
は任意のキャストに使え、Rustにおける最も危険なフィーチャの一つです!
型強制は暗黙に行われ、それ自体に構文はありませんが、as
で書くこともできます。
型強制が現れるのは、 let
・ const
・ static
文、関数呼び出しの引数、構造体初期化の際のフィールド値、そして関数の結果です。
一番よくある型強制は、参照からミュータビリティを取り除くものです。
&mut T
から &T
へ似たような変換としては、 生ポインタ からミュータビリティを取り除くものがあります。
*mut T
から *const T
へ参照も同様に、生ポインタへ型強制できます。
&T
から *const T
へ&mut T
から *mut T
へDeref
によって、カスタマイズされた型強制が定義されることもあります。
型強制は推移的です。
as
as
というキーワードは安全なキャストを行います。
let x: i32 = 5; let y = x as i64;
安全なキャストは大きく三つに分類されます。 明示的型強制、数値型間のキャスト、そして、ポインタキャストです。
キャストは推移的ではありません。
e as U1 as U2
が正しい式であったとしても、 e as U2
が必ずしも正しいとは限らないのです。
(実際、この式が正しくなるのは、 U1
が U2
へ型強制されるときのみです。)
e as U
というキャストは、 e
が型 T
を持ち、かつ T
が U
に 型強制 されるとき、有効です。
e as U
というキャストは、以下のどの場合でも有効です。
e
が型 T
を持ち、 T
と U
が数値型であるとき; numeric-caste
が C-likeな列挙型であり(つまり、ヴァリアントがデータを持っておらず)、 U
が整数型であるとき; enum-caste
の型が bool
か char
であり、 U
が整数型であるとき; prim-int-caste
が型 u8
を持ち、 U
が char
であるとき; u8-char-cast例えば、
fn main() { let one = true as u8; let at_sign = 64 as char; let two_hundred = -56i8 as u8; }let one = true as u8; let at_sign = 64 as char; let two_hundred = -56i8 as u8;
数値キャストのセマンティクスは以下の通りです。
驚くかもしれませんが、いくつかの制約のもとで、 生ポインタ と整数の間のキャストや、ポインタと他の型の間のキャストは安全です。 安全でないのはポインタの参照外しだけなのです。
fn main() { // let a = 300 as *const char; // a pointer to location 300 let a = 300 as *const char; // 300番地へのポインタ let b = a as u32; }let a = 300 as *const char; // 300番地へのポインタ let b = a as u32;
e as U
が正しいポインタキャストであるのは、以下の場合です。
e
が型 *T
を持ち、 U
が *U_0
であり、 U_0: Sized
または unsize_kind(T) == unsize_kind(U_0)
である場合; ptr-ptr-caste
が型 *T
を持ち、 U
が数値型で、 T: Sized
である場合; ptr-addr-caste
が整数、U
が *U_0
であり、 U_0: Sized
である場合; addr-ptr-caste
が型 &[T; n]
を持ち、 U
が *const T
である場合; array-ptr-caste
が関数ポインタ型であり、 U
が *T
であって、T: Sized
の場合; fptr-ptr-caste
が関数ポインタ型であり、 U
が整数型である場合; fptr-addr-casttransmute
as
は安全なキャストしか許さず、例えば4つのバイト値を u32
へキャストすることはできません。
let a = [0u8, 0u8, 0u8, 0u8]; let b = a as u32; // 4つの8で32になる
これは以下のようなメッセージがでて、エラーになります。
error: non-scalar cast: `[u8; 4]` as `u32`
let b = a as u32; // 4つの8で32になる
^~~~~~~~
これは「non-scalar cast」であり、複数の値、つまり配列の4つの要素、があることが原因です。 この種類のキャストはとても危険です。 なぜなら、複数の裏に隠れた構造がどう実装されているかについて仮定をおいているからです。 そのためもっと危険なものが必要になります。
transmute
関数は コンパイラ intrinsic によって提供されており、やることはとてもシンプルながら、とても恐ろしいです。
この関数は、Rustに対し、ある型の値を他の型であるかのように扱うように伝えます。
これは型検査システムに関係なく行われ、完全に使用者頼みです。
先ほどの例では、4つの u8
からなる配列が ちゃんと u32
を表していることを知った上で、キャストを行おうとしました。
これは、as
の代わりに transmute
を使うことで、次のように書けます。
use std::mem; unsafe { let a = [0u8, 0u8, 0u8, 0u8]; let b = mem::transmute::<[u8; 4], u32>(a); }
コンパイルを成功させるために、この操作は unsafe
ブロックでくるんであります。
技術的には、 mem::transmute
の呼び出しのみをブロックに入れればいいのですが、今回はどこを見ればよいかわかるよう、関連するもの全部を囲んでいます。
この例では a
に関する詳細も重要であるため、ブロックにいれてあります。
ただ、文脈が離れすぎているときは、こう書かないこともあるでしょう。
そういうときは、コード全体を unsafe
でくるむことは良い考えではないのです。
transmute
はほとんどチェックを行わないのですが、最低限、型同士が同じサイズかの確認はします。
そのため、次の例はエラーになります。
use std::mem; unsafe { let a = [0u8, 0u8, 0u8, 0u8]; let b = mem::transmute::<[u8; 4], u64>(a); }
エラーメッセージはこうです。
error: transmute called with differently sized types: [u8; 4] (32 bits) to u64
(64 bits)
ただそれ以外に関しては、自己責任です!