as によるダウンキャストは警告なしで値を切り捨てる
asによる整数の型変換(ダウンキャスト)は、変換先の型で表現できない値を警告もエラーもなく切り捨てます。300をu8にキャストすると、下位8ビットだけが残り44になります。
なぜエラーが出ないのか
出力: 44
(エラーなし)- Rustは何も報告しません。型としても借用としても正しいコードだからです
出力: 44- 実際の出力 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 fn main() { 2 let big: i32 = 300; 3 let small = big as u8 ; ^ 4 println!("{}", small); 5 }
u8で表現できるのは0〜255までです。300をasでu8にキャストすると、警告もエラーも無く下位8ビットだけが残ります。
直し方: u8 を i16 にします。
パターン2
1 fn main() { 2 let value: i32 = 500; 3 let result = value as u8 ; ^ 4 println!("{}", result); 5 }
asキャストは安全性チェックを行わない操作なので、範囲外の値は黙って切り捨てられます。
直し方: u8 を i16 にします。
パターン3
1 fn main() { 2 let code: i32 = 1000; 3 let byte_value = code as u8 ; ^ 4 println!("{}", byte_value); 5 }
範囲外の値をエラーとして検出したい場合は、asではなくtry_into()を使う必要があります。
直し方: u8 を i16 にします。
パターン4
1 fn main() { 2 let raw: i32 = 260; 3 let converted = raw as u8 ; ^ 4 println!("{}", converted); 5 }
260をu8にキャストすると4になります。この静かな切り捨てはバグとして非常に見つけにくいです。
直し方: u8 を i16 にします。
パターン5
1 fn main() { 2 let input: i32 = 400; 3 let narrowed = input as u8 ; ^ 4 println!("{}", narrowed); 5 }
ダウンキャストする前に、値が変換先の型の範囲に収まっているかを確認する習慣が必要です。
直し方: u8 を i16 にします。
よくある誤解
「範囲外の値をキャストしようとすれば、エラーになるかパニックするはず」という思い込みは誤りです。asキャストは意図的に安全性チェックを行わない操作で、範囲外の値は黙って切り捨てられます。安全に変換したい場合はtry_into()を使う必要があります。
実務での勘所
asによる切り捨ての仕組みは、値をそのままビット列として見て、変換先の型が扱えるビット数の下位ビットだけを残す、という単純な操作です。300は2進数で100101100(9ビット)ですが、u8は8ビットしか保持できないため、上位の1ビットが切り捨てられ下位8ビットの00101100だけが残り、これは10進数で44になります。300 % 256を計算した値と一致するのは偶然ではなく、下位ビットだけを残すという操作が、まさに256を法とした剰余を取ることと同じだからです。