Rustのキャスト(cast, 型変換)の使い方【as, From/Into】
目次
Rustのキャスト(型変換)の使い方
Rustには型がありますが、この型を別の型に変換することをキャストと言います。
静的型付けの言語ではこのようなキャストはプログラミングに深く関わり大事になってきます。
Rustではキャストをするのにas
というキーワードを使います。
また自作の構造体の型変換にはFrom/Intoを使います。
この記事ではRustの型変換について解説します。
関連記事
asの使い方
as
を使うと異なる方へキャストできます。
使い方は↓です。
変数 as 変換先の型;
たとえばi32
をu32
にキャストするには↓のようなコードを書きます。
// i32からu32へのキャスト let a: i32 = 123; // i32は符号付き32ビット整数 let b = a as u32; // u32は符号なし32ビット正数 println!("i32 to u32: {}", b); // i32 to u32: 123
i32
は符号付き32ビット整数です。
符号付きというのはプラスやマイナスがあるということです。
ですのでマイナスの表現に16ビット、プラスの表現に16ビット使われます。
u32
は符号なし32ビット正数です。
マイナスは表現できません。プラスの表現に32ビットが使われます。
↑の例では整数123
を保存した変数a
をu32
のb
に変換しています。
このような変換ではキャストが問題になることはありません。
ただしa
が負数だった場合、u32
は負数を表現できないので注意が必要です。
大きい型から小さい型へキャスト
キャストで問題が起こるのは、変換元の型と変換先の型の大きさが違う場合です。
このようなキャストでは値が変化することがあるので、キャストでは注意が必要になります。
キャストが原因で期待した値と違うものになってしまい、バグになってしまった。
なんてことがキャストの世界ではよく起こります。
大きな型から小さな型へキャストする場合は↓のようにコードを書きます。
// 大きな型から小さな型へのキャスト // 変換の際に上位ビットが切り捨てられる let a: i16 = 30000; // i16は符号付き16ビット整数 let b = a as i8; // i8は符号付き8ビット整数 println!("a: {:b}", a); // a: 111010100110000 println!("b: {:b}", b); // b: 110000
↑の例ではi16
をi8
へキャストしています。
i16
は符号付き16ビット整数、i8
は符号付き8ビット整数です。
つまりi16
をi8
で表現するには8ビット足りません。
このようなキャストの場合、Rustではどうなるのかと言いますと、小さい型への変換の際に上位ビットが切り捨てられます。
println!()
の{:b}
は数値を2進数で出力します。
↑の場合ですと111010100110000
の上位ビットが切り捨てられ110000
になっています。
小さい型から大きい型へのキャスト
大きい型から小さい型へキャストする場合は値が壊れる場合があります。
いっぽう、小さい型から大きい型へキャストする場合はそういったケースはまずありません。
注意したいのは符号の変化です。
↓はi8
をi16
にキャストするコードです。
// 小さな型から大きな型へのキャスト // 変換の際に上位ビットが符号ビットで埋められる let a: i8 = -127; let b = a as i16; println!("a: {}", a); // a: -127 println!("b: {}", b); // b: -127 println!("a: {:b}", a); // a: 10000001 println!("b: {:b}", b); // b: 1111111110000001
変換物とが負数だった場合は、変換先では上位ビットが符号ビットで埋められます。
FromとInto
構造体を別の型にキャストしたい場合があります。
そういう場合はFromとIntoを使います。
これはtraitで構造体に実装を行います。
モジュールは
use std::convert::{ From, Into };
↑のようにFromとIntoを使います。
Fromは特定の値から構造体へ変換する時に使います。
いっぽうIntoを特定の値を構造体へ変換するときに使います。
IntoについてはFromを実装すれば自動的に使えるようになります。
↓のように実装します。
use std::convert::{ From, Into }; // 整数をラップした構造体 #[derive(Debug)] struct Number { value: i32, } // Numberのfrom<i16>を実装 impl From<i16> for Number { fn from(x: i16) -> Self { Number { value: x as i32, // i16をi32へキャスト } } } fn main() { // From/Into let a: i16 = 100; let num1 = Number::from(a); // aからNumberに変換 let num2: Number = a.into(); // aをNumberに変換 println!("{:?}", num1); // Number { value: 100 } println!("{:?}", num2); // Number { value: 100 } }
Number
はvalue
というi32
のメンバを持った構造体です。
このNumber
に対してFrom<i16>
を実装します。
これはつまりi16
をNumber
に変換するための実装です。
// Numberのfrom<i16>を実装 impl From<i16> for Number { fn from(x: i16) -> Self { Number { value: x as i32, // i16をi32へキャスト } } }
↑のfrom()
の実装はシンプルです。
i16
型の引数x
を受け取り、それをi32
にキャストしてNumber
を生成します。
return
文を書いてませんがセミコロンを省略した場合Rustはそれを式として評価します。
関数の最後に式を書くとそれが関数の返り値になります。
fn main() { // From/Into let a: i16 = 100; let num1 = Number::from(a); // aからNumberに変換 let num2: Number = a.into(); // aをNumberに変換 println!("{:?}", num1); // Number { value: 100 } println!("{:?}", num2); // Number { value: 100 } }
↑のようにi16
型の変数a
をNumber::from(a)
でNumber
に変換しています。
また、a.into()
でa
をNumber
に変換しています。
println!()
の{:?}
はデバッグ出力を行います。
Number
には#[derive(Debug)]
が書かれているので、この書式が有効になります。
この出力では構造体がNumber { value: 100 }
のように出力されます。
↑を見るとわかりますが、into()
は実装していませんがfrom()
を実装しているのでinto()
を使えるようになっています。
おわりに
今回はRustのキャスト(cast, 型変換)について解説しました。
キャストは型付きの言語では必須の概念です。
押さえておきましょう。
(^ _ ^) | aからbへキャスト |
(・ v ・) | 符号に要注意 |