こんにちは! 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 | (アーキテクチャ依存) |
isize
と usize
のサイズは、プログラムが動作しているコンピュータのアーキテクチャ(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つの値のみを取ります: true
と false
。 通常、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つの基本的な複合型があります。
タプル (Tuple)
タプルは、様々な型の複数の値をまとめて1つの複合型にする一般的な方法です。タプルは固定長であり、一度宣言されるとそのサイズを変えることはできません。
タプルは丸括弧 ()
の中にカンマ区切りで値を並べて作成します。各位置には型があり、タプル内の要素の型がすべて同じである必要はありません。
// タプルを定義。型注釈も可能
let tup: (i32, f64, u8) = (500, 6.4, 1);
// パターンマッチングを使ってタプルを分解(デストラタリング)
let (x, y, z) = tup;
println!("The value of y is: {}", y); // y の値は 6.4
// ドット記法とインデックスを使って要素にアクセス
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
println!("First element: {}", five_hundred); // 最初の要素: 500
println!("Second element: {}", six_point_four); // 2番目の要素: 6.4
println!("Third element: {}", one); // 3番目の要素: 1
値を持たないタプル ()
はユニット型と呼ばれ、特別な意味を持ちます(例えば、値を返さない関数のデフォルトの戻り値型です)。
配列 (Array)
複数の値をまとめるもう一つの方法は配列です。タプルとは異なり、配列のすべての要素は同じ型でなければなりません。Rustの配列は他の言語の配列とは異なり、固定長です。
配列は角括弧 []
の中にカンマ区切りで値を並べて書きます。配列の型は [型; 長さ]
のように注釈できます。
// すべて同じ型の要素を持つ配列
let a = [1, 2, 3, 4, 5];
// 型と長さを明示的に指定
let months: [&str; 12] = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
// 同じ値を繰り返して初期化 [値; 個数]
let b = [3; 5]; // [3, 3, 3, 3, 3] と同じ
// インデックスを使って要素にアクセス(インデックスは0から始まる)
let first = a[0];
let second = a[1];
println!("First element of a: {}", first);
println!("Second element of a: {}", second);
// println!("Element at index 5: {}", a[5]); // これはエラー!インデックスが範囲外
println!("First month: {}", months[0]);
println!("Array b: {:?}", b); // デバッグ形式で配列全体を表示
配列はスタック上にデータが確保されるため、ヒープ上にデータを確保したい場合や、可変長のコレクションが必要な場合は、より柔軟なベクタ (Vector) 型を使用します(これは後のStepで学びます)。
文字列型 (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プログラミングの基礎となります。次のステップでは、これらの型を使ってプログラムの流れを制御する方法を見ていきましょう!