コンテンツにスキップ

Rustの型変換

Rustは、ある型を別の型へ自動で変換してはくれません。同じ式の中で i32 と f64 を混ぜると、コンパイルが通らないのです。変換は自分ではっきり書く必要があります。これはわざとそうなっています。ほかの言語では、気づかないうちに行われる型変換がバグのよくある原因になっているからです。

ある数値型を別の数値型にキャスト(変換)するには、as キーワードを使います。

fn main() {
let x: i32 = 10;
let y: f64 = x as f64; // i32 -> f64
println!("{y}");
}
▶ 実際に試してみよう

これは見た目以上に大切です。キャストせずに整数どうしで割り算をすると、おそらく期待していた小数の結果ではなく、整数の割り算になってしまいます。

fn main() {
let a = 10;
let b = 3;
println!("{}", a / b); // 3(整数の割り算)
println!("{}", a as f64 / b as f64); // 3.3333333333333335
}
▶ 実際に試してみよう

小さい型へのキャストではデータが失われることがある

Section titled “小さい型へのキャストではデータが失われることがある”

大きい型から小さい型へキャストしても(たとえば i32 から u8)、エラーにはなりません。収まらない部分が切り捨てられるだけです。そのため、びっくりするような結果になることがあります。

fn main() {
let big: i32 = 300;
let small = big as u8; // u8 には 0〜255 しか入らない
println!("{small}"); // 300 ではなく 44。一周して戻ってしまった
}
▶ 実際に試してみよう

浮動小数点数・文字・真偽値のキャスト

Section titled “浮動小数点数・文字・真偽値のキャスト”

as は整数以外の型にも使えます。それぞれに知っておきたいルールがあります。

fn main() {
println!("{}", 3.99_f64 as i32); // 3:小数点以下は切り捨て
println!("{}", -3.99_f64 as i32); // -3:小さいほうではなく、0の方向に切り捨て
println!("{}", 3.99_f64.round() as i32); // 4:四捨五入したいなら先に round する
println!("{}", 300.0_f64 as u8); // 255:大きすぎるので最大値で止まる
println!("{}", -5.0_f64 as u8); // 0:小さすぎるので最小値で止まる
println!("{}", 'A' as u32); // 65:文字のコード番号
println!("{}", 97_u8 as char); // a:逆向きの変換
println!("{}", true as i32); // 1(false なら 0)
}

実行結果:

3
-3
4
255
0
65
a
1
▶ 実際に試してみよう

ここで覚えておきたいポイントは次のとおりです。

  • 浮動小数点数から整数への変換では、小数点以下がすべて捨てられます。四捨五入はされません。いちばん近い整数にしたいなら、先に .round() を呼びましょう。
  • 範囲外の浮動小数点数は、収まるいちばん近い値に丸め込まれます(u8 では 300.0 が 255 になります)。一周して戻る整数とは違います(上の例では 300 が 44 になりました)。
  • 文字の裏には番号(Unicodeのコードポイント)があります。'A' as u32 は 65 になります。char にそのままキャストし直せるのは u8 だけです。もっと大きな数には char::from_u32 を使います。すべての数が有効な文字になるわけではないからです。
  • 真偽値は 1 か 0 になります。逆向きはできません。1 as bool はコンパイルが通らないので、代わりに n != 0 のような比較を使いましょう。

文字列を数値に変換する(パース)

Section titled “文字列を数値に変換する(パース)”

as によるキャストは、数値型どうしでしか使えません。テキストを数値に変換したいとき、たとえばユーザーが入力したものを数値にしたいときは、.parse() を使います。

fn main() {
let text = "42";
let number: i32 = text.parse().expect("有効な数値ではありません");
println!("{}", number + 8);
}

実行結果:

50
▶ 実際に試してみよう

.parse() は失敗することがあります(テキストがそもそも数値でなかったら?)。そのため、数値を直接返すのではなく Result を返します。Result はまだ説明していませんが、今のところは .expect("メッセージ") を「失敗したら、このメッセージを出してプログラムを止める」という簡単な書き方だと思っておいてください。きちんとした扱い方は Result のページで紹介します。

逆方向の変換はもっと簡単です。ほとんどの型は、.to_string() で String に変換できます。

fn main() {
let number = 42;
let text = number.to_string();
println!("{}", text + "!");
}
▶ 実際に試してみよう

3回のテストの点数 7、8、10 を i32 の値として保存してください。合計を表示し、続けて平均を小数点以下2桁で表示します。ヒント:整数どうしで割ると 8.33 ではなく 8 になってしまうので、先に合計を f64 にキャストしましょう。{:.2} で小数点以下2桁を表示できます。

解答を見る
fn main() {
let a: i32 = 7;
let b: i32 = 8;
let c: i32 = 10;
let sum = a + b + c;
let average = sum as f64 / 3.0; // 25 / 3 ではなく 25.0 / 3.0
println!("合計: {sum}");
println!("平均: {:.2}", average);
}

実行結果:

合計: 25
平均: 8.33
▶ 実際に試してみよう

お店から、価格がテキストの "4.50" で、個数が i32 の 3 で渡されました。テキストを f64 に変換して個数を掛け、合計: 13.5 と表示してください。ヒント:このページの2つの道具を両方使います。テキストには .parse()、個数には as f64 です。

解答を見る
fn main() {
let price_text = "4.50";
let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("有効な価格ではありません");
let total = price * quantity as f64; // f64 * f64
println!("合計: {total}");
}

実行結果:

合計: 13.5
▶ 実際に試してみよう

as f64 がなければ、Rustは f64 と i32 の掛け算を拒否します。Rustは、異なる数値型を勝手に混ぜることは決してしないからです。

  • Rustは型を自動で変換しません。変換するときは、いつも明示的にキャストします。
  • 数値型どうしの変換には as を使います(x as f64、x as u8)。
  • 小さい型へのキャストは、黙って切り捨てられます。エラーにならないので注意しましょう。
  • 浮動小数点数から整数への変換では小数点以下が切り捨てられます(四捨五入するなら先に .round())。char と bool も数値にキャストできます。
  • テキストを数値にするには .parse()、その逆には .to_string() を使います。

理解度チェック

1. Rustでキャストせずに 10 / 3 のように整数どうしを割ると、どうなりますか?

2. 300_i32 を as u8 でキャストすると、どうなりますか?

3. 文字列を数値に変換するとき、変換は失敗する可能性があるため、.parse() は何を返しますか?

スコア: 0 / 3