Rustは、メモリ安全性と実行パフォーマンスを高い次元で両立させた現代的なプログラミング言語として、2026年現在もシステム開発からWebアプリケーションまで幅広い分野で活用されています。
その安全性の根幹を支えているのが、コンパイル時に厳密なチェックを行う強力な型システムです。
プログラミングにおいて型を正しく理解することは、効率的なデバッグやバグの混入を防ぐだけでなく、Rust特有の所有権システムを使いこなすための第一歩となります。
本記事では、Rustで利用可能な基本データ型から、標準ライブラリで提供される主要な型までを体系的に整理して紹介します。
Rustの基本型(スカラー型)
スカラー型は、単一の値を表す型であり、Rustには整数型、浮動小数点型、論理値型、文字型の4つが存在します。
これらの型は、プログラムの最小単位としてメモリ上の固定サイズを占有します。
整数型
Rustの整数型は、符号の有無とビットサイズによって細かく分類されています。
符号あり整数(i)は負の数を扱うことができ、符号なし整数(u)は正の数のみを扱います。
| ビットサイズ | 符号あり | 符号なし |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| アーキテクチャ依存 | isize | usize |
日常的なプログラミングでは、パフォーマンスと精度のバランスが良いi32がデフォルトとして推奨されることが多いです。
isizeとusizeは、実行されるコンピュータのCPUアーキテクチャ(32ビットまたは64ビット)に依存する型で、主にコレクションのインデックス指定やメモリ上のオフセットに使用されます。
fn main() {
// 明示的な型指定
let a: i32 = -500;
let b: u32 = 1000;
// usizeは配列の要素数などに使用される
let list = [1, 2, 3];
let index: usize = 0;
println!("Value: {}", list[index]);
}
Value: 1
浮動小数点型
浮動小数点型には、32ビット精度のf32と、64ビット精度のf64があります。
現代のCPUではf64も高速に処理されるため、Rustでは精度を重視してデフォルトでf64が選択されるようになっています。
f32は単精度浮動小数点、f64は倍精度浮動小数点と呼ばれ、IEEE-754規格に準拠しています。
論理値型(bool)
論理値型は、true(真)またはfalse(偽)のいずれかの値を取る型です。
Rustの論理値型は1バイトのサイズを持ち、主に条件分岐やループの制御に使用されます。
文字型(char)
Rustのchar型は、単一の文字を表しますが、他の言語と異なりUnicodeのスカラー値を表すため、4バイトのサイズを持ちます。
これにより、日本語の漢字や絵文字なども1つのcharとして扱うことが可能です。
複合型(Compound Types)
複合型は、複数の値を一つの型にまとめることができる型です。
Rustには、タプルと配列の2つの主要な複合型が用意されています。
タプル型
タプルは、異なる型の値を一つのグループにまとめるために使用されます。
タプルの長さは固定であり、一度宣言するとそのサイズを変更することはできません。
各要素にはインデックス(0, 1, 2…)を使用してアクセスするか、パターンマッチングによる「デストラクト(解体)」を用いて値を取り出します。
fn main() {
// 異なる型を混在させることができる
let person: (&str, i32, bool) = ("Alice", 30, true);
// インデックスによるアクセス
println!("Name: {}", person.0);
// デストラクト
let (name, age, is_active) = person;
println!("Age: {}", age);
}
Name: Alice
Age: 30
配列型
配列は、同じ型の複数の値を一つの連続したメモリ領域に並べるための型です。
Rustの配列は固定長であり、コンパイル時にサイズが確定している必要があります。
スタック領域にデータを保持するため、要素数が決まっており、高速なアクセスが求められる場合に適しています。
fn main() {
// [型; 要素数] という形式で定義
let numbers: [i32; 5] = [10, 20, 30, 40, 50];
// すべて同じ値で初期化する構文
let buffer = [0; 1024]; // 0が1024個並んだ配列
println!("First element: {}", numbers[0]);
println!("Buffer size: {}", buffer.len());
}
標準ライブラリの重要なデータ型
基本型以外にも、Rustの標準ライブラリ(std)には、プログラム開発で欠かせない動的なデータ型が多数含まれています。
これらはヒープ領域を利用することで、実行時にサイズを変更できる柔軟性を持っています。
ベクタ(Vec<T>)
ベクタは、実行時にサイズを拡張または縮小できる動的配列です。
Rustにおいて最も頻繁に使用されるコレクションの一つであり、メモリ上の連続した領域に要素を格納します。
Vec<T>のTはジェネリクスと呼ばれ、任意の型を格納できることを示しています。
fn main() {
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);
// vec! マクロを使った初期化
let v2 = vec![10, 20, 30];
println!("Vector: {:?}", v2);
}
文字列型(String と &str)
Rustの文字列の扱いは、他の言語に比べて特徴的です。
主に、所有権を持つ動的なStringと、文字列の一部を参照するスライスである&strの2種類を使い分けます。
String型はヒープにデータを持ち、変更が可能ですが、&strは通常、バイナリ内に埋め込まれた文字列やStringの一部を指し示す不変の参照です。
fn main() {
// String型(所有権あり、変更可能)
let mut s = String::from("Hello");
s.push_str(", world!");
// &str型(参照、スライス)
let slice: &str = &s[0..5];
println!("Full string: {}", s);
println!("Slice: {}", slice);
}
ハッシュマップ(HashMap<K, V>)
ハッシュマップは、キー(Key)と値(Value)のペアを格納するコレクションです。
特定のキーに関連付けられたデータを効率的に検索したい場合に便利です。
デフォルトでは、サービス不能攻撃(DoS)を防ぐために安全なハッシュ関数が使用されています。
ユーザー定義型
Rustでは、開発者が独自のデータ構造を定義するために構造体と列挙型を使用します。
これらにより、複雑なドメインモデルを表現することが可能になります。
構造体(struct)
構造体は、複数の関連する値を一つの意味のある単位としてまとめるための仕組みです。
「名前付きフィールド構造体」「タプル構造体」「ユニット構造体」の3種類があります。
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
username: String::from("rust_user"),
email: String::from("rust@example.com"),
active: true,
sign_in_count: 1,
};
println!("User email: {}", user1.email);
}
列挙型(enum)
列挙型は、ある値がいくつかの候補のうちの一つであることを表現します。
Rustのenumは非常に強力で、各バリアントにデータを持たせることができます。
これにより、状態の遷移や複雑なエラー表現を安全に行うことができます。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::Write(String::from("Hello Enum"));
match msg {
Message::Write(text) => println!("Text: {}", text),
_ => (),
}
}
特殊な型とエラーハンドリング
Rustには、安全性を高めるために設計された「値が存在しない可能性」や「エラーが発生する可能性」を明示するための型があります。
Option<T>
Rustには他の言語にあるようなnullが存在しません。
その代わりに、値がある(Some)か、ない(None)かを表すOption<T>型を使用します。
これにより、コンパイラは開発者に対して「値がない場合」の処理を強制し、実行時のヌルポインタ例外を完全に排除します。
Result<T, E>
Result<T, E>は、操作が成功したか失敗したかを表すための型です。
成功した場合はOk(T)を返し、失敗した場合はErr(E)を返します。
Rustのエラーハンドリングは例外(Exception)を使用せず、このResult型を戻り値として伝搬させるスタイルが基本です。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {
if denominator == 0.0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(numerator / denominator)
}
}
fn main() {
match divide(10.0, 2.0) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}
スマートポインタ
Rustのメモリ管理において、ポインタをより安全かつ便利に扱うための「スマートポインタ」も重要な型です。
Box<T>
Box<T>は、データをヒープ領域に配置するための最も単純なスマートポインタです。
データのサイズがコンパイル時に不明な場合や、大きなデータをコピーせずに所有権だけを移動させたい場合に使用されます。
Rc<T> と Arc<T>
通常、Rustの所有権は一人の持ち主しか認めませんが、複数の場所で値を共有したい場合があります。
Rc<T>(Reference Counted)は、参照カウントを用いてシングルスレッド内での共有を可能にします。
Arc<T>(Atomic Reference Counted)は、スレッドセーフな参照カウントを提供し、マルチスレッド環境でのデータ共有を実現します。
まとめ
Rustの型システムは、一見すると非常に厳格で学習コストが高いように感じられるかもしれません。
しかし、数値型から始まり、柔軟なStringやVec、そして安全性を担保するOptionやResultまでを理解することで、プログラムの堅牢性は劇的に向上します。
基本型を適切に使い分け、構造体や列挙型でデータの意味を定義し、スマートポインタでメモリを制御することが、Rustマスターへの道となります。
本記事で紹介した各型の特徴を意識しながらコードを書くことで、コンパイラを味方につけた効率的な開発が可能になるでしょう。
最新のRust 2026年時点のエコシステムにおいても、これらの基本原則は変わらず、より洗練されたライブラリ群がこれらの型を基礎として構築されています。
まずは基本となるスカラー型と複合型、そして頻出するStringとVecから手に馴染ませていくことをお勧めします。
