カテゴリ: Rust 更新日: 2026/03/28

Rustのタプル型(Tuple)完全ガイド!複数の値をまとめて扱う変数の使い方とデータ型を解説

Rustのタプル型とは何か?複数の値をまとめる方法を解説
Rustのタプル型とは何か?複数の値をまとめる方法を解説

先生と生徒の会話形式で理解しよう

生徒

「Rustでプログラミングをしているときに、整数とか文字列とか、違う種類のデータをひとまとめにして扱いたい場面があるんです。配列だと同じ型しか入れられないし、どうすればいいですか?」

先生

「そういう時に便利なのがタプル(Tuple)というデータ型です。タプルを使えば、異なる型の複数の値を、一つの変数としてまとめて管理できますよ。」

生徒

「へぇ、違う型でも大丈夫なんですね!でも、それって構造体(Struct)とは何が違うんですか?」

先生

「構造体のようにフィールドに名前を付ける必要がなく、もっと手軽にグループ化できるのが特徴です。特に関数の戻り値で複数の値を返したい時などに大活躍します。今回はこのタプルの使い方を基本から分解の方法まで詳しく見ていきましょう!」

1. Rustのタプル型(Tuple)とは何か?

1. Rustのタプル型(Tuple)とは何か?
1. Rustのタプル型(Tuple)とは何か?

Rustにおけるタプル(Tuple)は、複数の値を順序付けて一つの複合的な値としてまとめるためのデータ型です。Rustを学習する上で、基本的なデータ構造の一つとして必ず理解しておく必要があります。

タプルの最大の特徴は、「異なるデータ型を一つのグループに混在させることができる」という点です。例えば、i32型の整数、f64型の浮動小数点数、そしてchar型の文字といった全く性質の異なるデータを、一つのタプル変数の中に格納することが可能です。

また、タプルは一度宣言すると、そのサイズ(長さ)は固定されます。後から要素を追加したり、削除したりしてサイズを変更することはできません。これはRustのメモリ安全性を保つための仕様であり、コンパイル時にサイズが確定していることで、スタック領域への割り当てなどが効率的に行われます。

Pythonなどの他の動的型付け言語を使っていた方にとっては馴染み深い概念かもしれませんが、Rustは静的型付け言語であるため、タプル内の各要素の型もコンパイル時に明確に決定されます。これにより、意図しない型のデータの混入を防ぐことができ、バグの少ない堅牢なコードを書く助けとなります。

2. タプルの定義と型注釈の書き方

2. タプルの定義と型注釈の書き方
2. タプルの定義と型注釈の書き方

実際にRustでタプルを作成する方法を見ていきましょう。タプルは丸括弧()を使用し、その中に各要素をカンマ,で区切って記述します。

Rustの強力な型推論機能のおかげで、通常は型注釈を明示的に書かなくても、コンパイラが右辺の値から自動的に型を判断してくれます。しかし、意図的に特定の型(例えばi32ではなくu8など)として扱いたい場合や、コードの可読性を高めるために、明示的に型注釈を書くことも推奨されます。

以下のサンプルコードでは、商品ID(整数)、価格(浮動小数点数)、商品名(文字列スライス)を一つのタプルにまとめています。


fn main() {
    // 型推論に任せる場合
    let implicit_tuple = (500, 6.5, "Apple");

    // 型注釈を明記する場合: (i32, f64, &str)
    let explicit_tuple: (i32, f64, &str) = (1001, 120.5, "Orange");

    // デバッグ出力で見やすいように {:?} を使用
    println!("暗黙的なタプル: {:?}", implicit_tuple);
    println!("明示的なタプル: {:?}", explicit_tuple);
}

出力結果は以下のようになります。


暗黙的なタプル: (500, 6.5, "Apple")
明示的なタプル: (1001, 120.5, "Orange")

このように、let 変数名: (型1, 型2, ...) = (値1, 値2, ...);という構文で定義します。型の並び順と、値の並び順は完全に一致している必要があります。もし型と値の順序が異なれば、コンパイルエラーとなり、プログラムは実行されません。これがRustの厳格な型システムのメリットです。

3. インデックスを使った要素へのアクセス方法

3. インデックスを使った要素へのアクセス方法
3. インデックスを使った要素へのアクセス方法

タプルの中に格納された個々の値(要素)を取り出したい場合、最も基本的な方法はドット記法(ピリオド)とインデックス(添字)を使用することです。

配列などと同様に、Rustのタプルのインデックスは0から始まります。つまり、最初の要素にアクセスするには.0、2番目の要素には.1を指定します。変数名の後にピリオドを打ち、続けて数字を書くことで直接値を取得できます。

この方法は、タプルの要素数が少ない場合や、特定の場所にある値だけが必要な場合に非常に簡潔に記述できるため便利です。ただし、存在しないインデックス(例えば要素数が3つなのに.3を指定するなど)にアクセスしようとすると、コンパイルエラーが発生します。Rustは実行時エラーになる前に、コンパイルの段階でこの間違いを指摘してくれるため、安全性が高いと言えます。


fn main() {
    // RGBの色情報をタプルで表現 (Red, Green, Blue)
    let color = (255, 128, 0);

    // インデックスを使って各要素にアクセス
    let red = color.0;
    let green = color.1;
    let blue = color.2;

    println!("赤: {}, 緑: {}, 青: {}", red, green, blue);
}

出力結果は以下の通りです。


赤: 255, 緑: 128, 青: 0

このように、color.0のように記述することで、タプルの中身を個別に利用できます。構造体のフィールド名のような意味のある名前はありませんが、順序が決まっているデータ(座標x, yや、RGB値など)を扱うには十分な機能を持っています。

4. パターンマッチングによるタプルの分解

4. パターンマッチングによるタプルの分解
4. パターンマッチングによるタプルの分解

タプルの要素を取り出すもう一つの、そしてより「Rustらしい」強力な方法が、パターンマッチングを使用した分解(Destructuring)です。

分解を利用すると、タプルの全ての要素を一度に別々の変数へ代入することができます。これにより、コードがすっきりと読みやすくなります。特に要素数が多いタプルや、関数の戻り値としてタプルを受け取った後に、それぞれの値を個別の変数として使いたい場合に非常に役立ちます。

構文はlet (変数1, 変数2, 変数3) = タプル変数;のようになります。左辺の括弧内の変数の数は、右辺のタプルの要素数と完全に一致していなければなりません。

以下の例では、3つの要素を持つタプルを分解し、それぞれx, y, zという変数に代入しています。


fn main() {
    // 3次元座標を表すタプル
    let point = (10, 20, 30);

    // タプルを分解して、個別の変数 x, y, z に値を束縛
    let (x, y, z) = point;

    println!("座標 X: {}", x);
    println!("座標 Y: {}", y);
    println!("座標 Z: {}", z);
}

出力結果です。


座標 X: 10
座標 Y: 20
座標 Z: 30

この「分解」のテクニックは、Rustのプログラミングにおいて頻繁に使用されます。例えば、不要な値がある場合は、変数名の代わりにアンダースコア_を使用することで、その値を無視(破棄)することも可能です。let (x, _, z) = point;と書けば、2番目の要素は無視され、変数の無駄な宣言を防ぐことができます。

5. タプルのミュータブル(可変)性について

5. タプルのミュータブル(可変)性について
5. タプルのミュータブル(可変)性について

Rustの変数はデフォルトでイミュータブル(不変)ですが、これはタプルにも適用されます。一度作成したタプルの要素を後から書き換えたい場合は、タプル変数を宣言する際にmutキーワードを付けて、ミュータブル(可変)にする必要があります。

ここで重要な注意点があります。mutを使って可変にしたとしても、「変更できるのは値だけであり、型は変更できない」ということです。また、タプルのサイズ(要素数)も変更できません。つまり、最初に(i32, f64)として定義されたタプルの1番目の要素に、文字列や別の型を代入しようとするとコンパイルエラーになります。

あくまで、同じ型の別の値に書き換えることだけが許可されます。これはRustの静的型付けシステムのルールが、タプルの中身に対しても厳密に適用されているためです。


fn main() {
    // mutキーワードで可変のタプルを宣言
    let mut status = (200, "OK");
    println!("変更前: コード={}, メッセージ={}", status.0, status.1);

    // 値を書き換える
    status.0 = 404;
    status.1 = "Not Found";
    
    // 以下のように型が違う値を入れようとするとエラーになります
    // status.0 = "Error"; // コンパイルエラー!

    println!("変更後: コード={}, メッセージ={}", status.0, status.1);
}

出力結果です。


変更前: コード=200, メッセージ=OK
変更後: コード=404, メッセージ=Not Found

このように、状態が変化するデータをペアで管理したい場合(例えばゲーム内の座標(x, y)を移動に合わせて更新するなど)には、mut付きのタプルが非常に有効です。

6. 関数からの戻り値としてタプルを活用する

6. 関数からの戻り値としてタプルを活用する
6. 関数からの戻り値としてタプルを活用する

タプルが最も輝く場面の一つが、「関数から複数の値を返したいとき」です。多くのプログラミング言語では、関数は基本的に一つの値しか返せません(複数の値を返すために、専用のクラスを作ったり、ポインタ引数を使ったりする言語もあります)。

しかしRustでは、タプルを使うことで、専用の構造体を定義することなく、手軽に複数の値を戻り値としてまとめることができます。これにより、関数の役割を柔軟に設計することが可能になります。

例えば、文字列を受け取って「その長さ」と「その文字列そのもの」をペアにして返す関数を考えてみましょう。タプルを使えば非常に直感的です。


fn main() {
    let s = String::from("Rust Language");
    
    // 関数からタプルを受け取る
    let (len, original_text) = calculate_length(s);

    println!("テキスト: '{}', 長さ: {}", original_text, len);
}

// 文字列の所有権を受け取り、(長さ, 文字列) のタプルを返す関数
fn calculate_length(s: String) -> (usize, String) {
    let length = s.len();
    (length, s) // タプルを作成して返す
}

出力結果です。


テキスト: 'Rust Language', 長さ: 13

このように、タプルを戻り値にすることで、関連するデータをひとまとめにして呼び出し元に渡すことができます。呼び出し元では、先ほど解説した「分解(Destructuring)」を使って、即座に個別の変数として扱うことができるため、非常にスムーズなデータの受け渡しが実現できます。

7. ユニット型(Unit Type)という特別なタプル

7. ユニット型(Unit Type)という特別なタプル
7. ユニット型(Unit Type)という特別なタプル

最後に、タプルに関連する重要な概念として「ユニット型」について解説します。Rustには、要素を一つも持たない空のタプル()が存在します。これをユニット(Unit)と呼び、その型もまた()と書きます。

ユニット型は、「意味のある値を何も持っていない」ことを表すために使われます。他の言語(C言語やJavaなど)におけるvoidに近い概念です。Rustでは、戻り値を明示的に指定しない関数は、暗黙的にこのユニット型()を返していることになっています。

例えば、単に画面に文字を表示するだけの関数や、副作用(変数の書き換えなど)のみを目的とした式は、評価されると()になります。初心者のうちはあまり意識しないかもしれませんが、Rustのコンパイラのエラーメッセージなどで「expected `()`, found ...」といった表現を見かけることがあるでしょう。これは「ここでは値がないこと(ユニット)が期待されているのに、何か値が返されていますよ」という意味です。

空のタプルであるユニット型は、メモリ上のサイズも0であり、実行時のオーバーヘッドはありません。Rustの型システムにおいて、「無」を安全かつ明確に表現するための重要なパーツとなっているのです。

8. 配列や構造体との使い分けのポイント

8. 配列や構造体との使い分けのポイント
8. 配列や構造体との使い分けのポイント

ここまでタプルについて詳しく学んできましたが、Rustには他にも複数の値を扱う「配列(Array)」や「構造体(Struct)」があります。これらをどのように使い分けるべきか、迷うこともあるでしょう。

まず、配列との違いは「型」と「サイズ」です。配列は全ての要素が「同じ型」でなければなりませんが、タプルは「異なる型」を組み合わせることができます。もし、全てのデータが同じ型(例えば数値のリスト)であれば配列やベクタを使うべきですが、数値と文字列をセットにしたい場合はタプルが適しています。

次に、構造体との違いは「フィールドの名前」です。構造体は各フィールドにnameageといった名前を付けられますが、タプルは順序だけです。長期間メモリに残るデータや、他人が読むコードでデータの意味を明確にしたい場合は、構造体を定義する方が親切です。

一方で、関数内での一時的な計算結果のペアや、その場限りのデータのグルーピングには、わざわざ構造体を定義するのは大袈裟です。そのような「軽量で一時的なデータのまとまり」には、タプルが最適解となります。適材適所で使い分けることで、Rustのコードはより美しく、効率的になります。

カテゴリの一覧へ
新着記事
New1
C++
C++の関数の作り方を完全ガイド!初心者でもわかる基本構文と定義方法
New2
Rust
Rustのビット演算子とビット操作を徹底解説!低レイヤ開発への第一歩
New3
C言語
C言語の配列名はポインタ?暗黙の変換を初心者向けにわかりやすく解説
New4
C++
C++が今でも現役で使われる理由を徹底解説!長年愛されるプログラミング言語の魅力
人気記事
No.1
Java&Spring記事人気No1
C言語
C言語を学ぶ初心者におすすめの環境構築手順【2025年版】
No.2
Java&Spring記事人気No2
C言語
C言語のソースコードとヘッダファイルの役割とは?初心者向けにわかりやすく解説!
No.3
Java&Spring記事人気No3
C++
MinGWとMSYS2でWindowsにC++環境を構築する方法を徹底解説!初心者でもできるセットアップガイド
No.4
Java&Spring記事人気No4
C言語
Visual Studio CodeでC言語を実行する方法【拡張機能の設定と実行手順】
No.5
Java&Spring記事人気No5
C言語
LinuxでC言語開発環境を構築する方法【GCCとMakefileの基本】
No.6
Java&Spring記事人気No6
C言語
C言語開発でよく使われるエディタとIDEランキング【初心者向け完全ガイド】
No.7
Java&Spring記事人気No7
C++
CMakeの基本構文とCMakeLists.txtを初心者向けに解説
No.8
Java&Spring記事人気No8
C言語
C言語をオンラインで実行できる便利なコンパイラサービスまとめ【初心者向け】