[Rustのはじめ方] Part6: データ型(数値型・文字列型・ブール型など)

こんにちは! Rustの学習ジャーニーへようこそ! このセクションでは、Rustの基本的なデータ型について学んでいきます。 Rustは静的型付け言語です。これは、コンパイル時にすべての変数の型が分かっている必要があることを意味します。でも心配しないでください!多くの場合、コンパイラは私たちが使いたい型を値や使われ方から推論してくれます。

Rustのデータ型は大きく分けてスカラー型複合型の2つのカテゴリがあります。

スカラー型 (Scalar Types)

スカラー型は、単一の値を表現します。Rustには4つの主要なスカラー型があります。

  • 整数型 (Integer Types)
  • 浮動小数点数型 (Floating-Point Types)
  • 論理値型 (Boolean Type)
  • 文字型 (Character Type)

整数型 (Integer Types)

整数は小数点のない数値です。Rustの整数型には、符号付き (i)符号無し (u) があります。符号付きは負の数も表現でき、符号無しは0以上の値のみを表現します。

各整数型は、格納できるビット数によって名前が付けられています。

ビット数 符号付き 符号無し 最小値 (符号付き) 最大値 (符号付き) 最小値 (符号無し) 最大値 (符号無し)
8-bit i8 u8 -128 127 0 255
16-bit i16 u16 -32,768 32,767 0 65,535
32-bit i32 u32 -2,147,483,648 2,147,483,647 0 4,294,967,295
64-bit i64 u64 -9,223,372,036,854,775,808 9,223,372,036,854,775,807 0 18,446,744,073,709,551,615
128-bit i128 u128 -2127 2127 – 1 0 2128 – 1
arch isize usize (アーキテクチャ依存) (アーキテクチャ依存) 0 (アーキテクチャ依存)

isizeusize のサイズは、プログラムが動作しているコンピュータのアーキテクチャ(32ビットか64ビットか)に依存します。コレクションのサイズ指定などによく使われます。

型を指定しない場合、整数リテラルはデフォルトで i32 型になります。 明示的に型を指定することも、型推論に任せることもできます。また、数値リテラルにサフィックス(例: 57u8)を付けて型を指定することも可能です。

// 型を明示的に指定
let age: u32 = 30;

// 型推論(コンパイラが i32 と推論)
let score = -10;

// サフィックスで型を指定
let player_id = 12345i64;

// 見やすいように `_` を区切り文字として使える
let large_number = 1_000_000;

// 他の進数でのリテラル
let hex = 0xff;       // 16進数
let octal = 0o77;      // 8進数
let binary = 0b1111_0000; // 2進数
let byte = b'A';       // バイト (u8 のみ)

println!("Age: {}", age);
println!("Score: {}", score);
println!("Player ID: {}", player_id);
println!("Large Number: {}", large_number);
println!("Hex: {}", hex);
println!("Octal: {}", octal);
println!("Binary: {}", binary);
println!("Byte: {}", byte);
整数オーバーフローについて
Rustでは、整数型の範囲を超える値を代入しようとするとオーバーフローが発生します。デバッグモードではパニック(プログラムのクラッシュ)しますが、リリースモード (--release) では2の補数によるラップアラウンド(例えば u8 で 255 + 1 が 0 になる)が発生します。これは意図しないバグの原因になることがあるため注意が必要です。

浮動小数点数型 (Floating-Point Types)

Rustには2つの浮動小数点数型があります。

  • f32: 単精度浮動小数点数 (32ビット)
  • f64: 倍精度浮動小数点数 (64ビット)

デフォルトの型は f64 です。現代のCPUでは f32 とほぼ同じ速度でありながら、より高い精度を持つため、一般的に f64 が推奨されます。

let x = 2.0; // f64 (デフォルト)

let y: f32 = 3.0; // f32 (明示的に指定)

println!("x = {}", x);
println!("y = {}", y);

論理値型 (Boolean Type)

Rustの論理値型 bool は、2つの値のみを取ります: truefalse。 通常、if 式などの条件分岐で使われます。

let is_rust_fun: bool = true;
let is_learning_hard = false;

if is_rust_fun {
    println!("Rust is fun! ");
} else {
    println!("Maybe give it more time?");
}

println!("Is learning hard? {}", is_learning_hard);

文字型 (Character Type)

Rustの char 型は、最も基本的な文字型です。注意点として、char は単一のUnicodeスカラー値を表現するため、4バイトのサイズを持ちます。これはASCII文字だけでなく、絵文字やアクセント付き文字なども含むことができることを意味します。

文字リテラルはシングルクォート (') で囲みます。ダブルクォート (") は文字列リテラル(後述)に使われます。

let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = ''; // 絵文字もchar!

println!("c = {}", c);
println!("z = {}", z);
println!("Heart Eyed Cat = {}", heart_eyed_cat);

複合型 (Compound Types)

複合型は、複数の値を1つの型にまとめることができます。Rustには主に2つの基本的な複合型があります。

文字列型 (String Types)

Rustには主に2つの文字列型があります: String&str (文字列スライス)。これらの違いを理解することはRustを学ぶ上で重要です。どちらの型もUTF-8エンコードされた文字列を扱います。

&str (文字列スライス)

&str は「文字列スライス」と呼ばれ、他の場所に格納されているUTF-8文字列データへの参照 (borrowed reference) です。文字列リテラル ("hello" のようなコード中の文字列) は &str 型です。これらのリテラルはプログラムのバイナリに直接埋め込まれるため、静的で不変です。

&str は基本的に、データへのポインタとその長さで構成されています。これは不変のビューであり、通常、文字列データを変更する必要がない場合に適しています。関数の引数として文字列を受け取る場合によく使われます。

let s1 = "Hello, world!"; // s1 の型は &str
let s2: &str = "こんにちは";

fn print_message(msg: &str) {
    println!("Message: {}", msg);
}

print_message(s1);
print_message(s2);

String

String 型は、ヒープ上に確保された、所有権を持つ (owned)、伸長可能で、変更可能なUTF-8文字列バッファです。文字列データをプログラム実行時に作成したり、変更したりする必要がある場合に使用します。

String&str から作成できます。

// 空の String を作成
let mut s = String::new();

// 文字列リテラル (&str) から String を作成
let data = "initial contents";
let s1 = data.to_string();
// このメソッドは文字列リテラルに対しても直接機能する
let s2 = "initial contents".to_string();
// String::from を使う方法もある
let s3 = String::from("initial contents");

// String は変更可能 (mut をつける必要がある)
let mut greeting = String::from("Hello");
greeting.push_str(", world!"); // 文字列スライス (&str) を末尾に追加
greeting.push('!');          // char を末尾に追加

println!("{}", greeting); // Hello, world!!

// String は所有権を持つ
fn takes_ownership(some_string: String) {
    println!("Got ownership of: {}", some_string);
    // some_string はここでスコープを抜け、メモリが解放される
}

let owned_string = String::from("my string");
// takes_ownership(owned_string);
// println!("{}", owned_string); // これはエラー!所有権が移動したため

// &String から &str を作成できる (Deref Coercion)
fn takes_slice(slice: &str) {
    println!("Got slice: {}", slice);
}

let my_string = String::from("example");
takes_slice(&my_string); // String の参照を渡せる
takes_slice(&my_string[0..4]); // スライスも渡せる ("exam")
takes_slice(my_string.as_str()); // 明示的に &str に変換

String&str の使い分けは、所有権の概念と密接に関連しています。これはStep 3で詳しく学びますので、今は「変更が必要なら String、単に見るだけなら &str」くらいの感覚で大丈夫です!

まとめ

このセクションでは、Rustの基本的なデータ型について学びました。

  • スカラー型: 整数 (i32, u8 など)、浮動小数点数 (f64, f32)、論理値 (bool)、文字 (char)
  • 複合型: タプル (異なる型を集めた固定長の組)、配列 (同じ型を集めた固定長のリスト)
  • 文字列型: 文字列スライス (&str、不変の参照) と String (変更可能な所有された文字列)

これらの基本的な型は、Rustプログラミングの基礎となります。次のステップでは、これらの型を使ってプログラムの流れを制御する方法を見ていきましょう!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です