ユーニックス総合研究所

  • home
  • archives
  • rust-cast

Rustのキャスト(cast, 型変換)の使い方【as, From/Into】

  • 作成日: 2022-05-20
  • 更新日: 2023-12-25
  • カテゴリ: Rust

Rustのキャスト(型変換)の使い方

Rustには型がありますが、この型を別の型に変換することをキャストと言います。
静的型付けの言語ではこのようなキャストはプログラミングに深く関わり大事になってきます。

Rustではキャストをするのにasというキーワードを使います。
また自作の構造体の型変換にはFrom/Intoを使います。

この記事ではRustの型変換について解説します。

関連記事

文字列の描画 - Rustで作るWindowsアプリ
改行付きの文字列を描画する - Rustで作るWindowsアプリ
ラベルの表示 - Rustで作るWindowsアプリ

文字列の描画 - Rustで作るWindowsアプリ
改行付きの文字列を描画する - Rustで作るWindowsアプリ
ラベルの表示 - Rustで作るWindowsアプリ

asの使い方

asを使うと異なる方へキャストできます。
使い方は↓です。

変数 as 変換先の型;  

たとえばi32u32にキャストするには↓のようなコードを書きます。

    // 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を保存した変数au32bに変換しています。
このような変換ではキャストが問題になることはありません。
ただし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  

↑の例ではi16i8へキャストしています。
i16は符号付き16ビット整数、i8は符号付き8ビット整数です。
つまりi16i8で表現するには8ビット足りません。

このようなキャストの場合、Rustではどうなるのかと言いますと、小さい型への変換の際に上位ビットが切り捨てられます。
println!(){:b}は数値を2進数で出力します。
↑の場合ですと111010100110000の上位ビットが切り捨てられ110000になっています。

小さい型から大きい型へのキャスト

大きい型から小さい型へキャストする場合は値が壊れる場合があります。
いっぽう、小さい型から大きい型へキャストする場合はそういったケースはまずありません。
注意したいのは符号の変化です。

↓はi8i16にキャストするコードです。

    // 小さな型から大きな型へのキャスト  
    // 変換の際に上位ビットが符号ビットで埋められる  
    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 }  
}  

Numbervalueというi32のメンバを持った構造体です。
このNumberに対してFrom<i16>を実装します。
これはつまりi16Numberに変換するための実装です。

// 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型の変数aNumber::from(a)Numberに変換しています。
また、a.into()aNumberに変換しています。

println!(){:?}はデバッグ出力を行います。
Numberには#[derive(Debug)]が書かれているので、この書式が有効になります。
この出力では構造体がNumber { value: 100 }のように出力されます。

↑を見るとわかりますが、into()は実装していませんがfrom()を実装しているのでinto()を使えるようになっています。

おわりに

今回はRustのキャスト(cast, 型変換)について解説しました。
キャストは型付きの言語では必須の概念です。
押さえておきましょう。

🦝 < aからbへキャスト

🐭 < 符号に要注意