Rustのタプル
タプル(tuple)は、いくつかの値をひとつにまとめたものです。まとめる値は、それぞれ違う型でもかまいません。わざわざ新しい型を定義しなくても、関係のある値をさっと束ねられる便利な道具です。
タプルを作る
Section titled “タプルを作る”fn main() { let person: (&str, i32, bool) = ("Alice", 30, true); println!("{:?}", person);}実行結果:
("Alice", 30, true)このタプルには &str、i32、bool という3つの違う型が入っていますね。これは配列にはできないことです。配列の要素は、すべて同じ型でなければならないからです。
タプル全体を表示するには、{} ではなく {:?}(デバッグ表示)を使います。ふつうの {} はタプルには使えません。
位置で要素を取り出す
Section titled “位置で要素を取り出す”.0、.1、.2 のように書きます。タプルの番号は0から始まります。
fn main() { let person = ("Alice", 30, true);
println!("名前: {}", person.0); println!("年齢: {}", person.1); println!("有効: {}", person.2);}実行結果:
名前: Alice年齢: 30有効: true位置は、コードに直接書いた数字でなければなりません。person.i のように変数は使えません。位置ごとに違う型が入っているかもしれず、Rustはコンパイルの時点で型を知る必要があるからです。
タプルを分解する
Section titled “タプルを分解する”すべての値を、1行でそれぞれ別の変数に取り出せます。これを分解(destructuring)と呼びます。
fn main() { let person = ("Alice", 30, true); let (name, age, is_active) = person;
println!("{name}は{age}歳、有効: {is_active}");}実行結果:
Aliceは30歳、有効: true.0、.1、.2 を何度も書くより、このほうが読みやすいことがよくあります。要素が3つを超えるようなタプルでは、特にそうです。
関数から複数の値を返す
Section titled “関数から複数の値を返す”関数が宣言できる戻り値の型はひとつだけです。そこで、複数の値を返したいときはタプルを使うのが自然です。
fn min_max(numbers: &[i32]) -> (i32, i32) { let min = *numbers.iter().min().unwrap(); let max = *numbers.iter().max().unwrap(); (min, max)}
fn main() { let (smallest, largest) = min_max(&[4, 8, 1, 9, 3]); println!("最小: {smallest}、最大: {largest}");}実行結果:
最小: 1、最大: 9&[i32]、.iter()、.unwrap() は、今は気にしなくて大丈夫です。それぞれスライス、イテレータ、Optionのページで扱います。ここで注目してほしいのは、戻り値の型 (i32, i32) と、main がそれをすぐに2つの変数に分解しているところです。
タプルを変更する・値を入れ替える
Section titled “タプルを変更する・値を入れ替える”タプルを mut で宣言すれば、同じ .0、.1 の位置を使って、要素を1つずつ変更できます。変えられるのは値だけで、形は変えられません。タプルの要素の数と型は、最初に作ったときのまま変わりません。
分解は逆向きにも使えます。つまり、すでにある変数に向けて代入できるのです。これを使うと、一時的な変数なしで、2つの値を1行で入れ替えられます。一部の要素だけが欲しいときは、いらない要素の位置に _ と書きます。
fn main() { let mut point = (3, 4); point.0 += 10; // 1つの要素をその場で変更する println!("point は {:?}", point);
let mut a = 1; let mut b = 2; (a, b) = (b, a); // 2つの値を入れ替える println!("a = {a}, b = {b}");
let (x, _) = point; // x は残し、2つ目の要素は無視する println!("x = {x}");}実行結果:
point は (13, 4)a = 2, b = 1x = 13入れ替えがうまくいくのは、Rustが先に右辺の (b, a) を作るからです。そのあとで初めて、それを a と b に分解します。_ やパターンを使ってできることは、パターンマッチングのページでもっとたくさん紹介しています。
空のタプル: ()
Section titled “空のタプル: ()”実は、あなたはもう気づかないうちにタプルに出会っています。() はユニット型(unit type)と呼ばれ、意味のある値を何も返さない関数が返すものです。fn main() の戻り値の型も、書かれていないだけで実は () です。
タプルと構造体の使い分け
Section titled “タプルと構造体の使い分け”タプルは、名前をつけるほどでもないちょっとしたまとまりにぴったりです。特に、関数から2つか3つの値を返すときに活躍します。一方、値のまとまりが「名前と年齢を持つ Person」のように、コードのあちこちで名前で参照するはっきりした概念を表すなら、構造体のほうが読みやすく、たいていはよい選択です。
1. 和と積
Section titled “1. 和と積”2つの数の和と積を両方返す関数 sum_and_product(a: i32, b: i32) -> (i32, i32) を書いてください。main で5と6を渡して呼び出し、結果を分解しましょう。ヒント:関数の最後の行は、タプルそのもの (a + b, a * b) です。セミコロンは付けません。
解答を見る
fn sum_and_product(a: i32, b: i32) -> (i32, i32) { (a + b, a * b)}
fn main() { let (sum, product) = sum_and_product(5, 6); println!("和 = {sum}、積 = {product}");}実行結果:
和 = 11、積 = 302. 2つの角で表す長方形
Section titled “2. 2つの角で表す長方形”長方形は、2つの点のタプルとして表せます。それぞれの点もタプルです:let rect = ((1, 2), (5, 5));。この長方形の幅、高さ、面積を求めてください。ヒント:タプルは入れ子にできます。rect.1.0 は「2つ目の点の1つ目の要素」という意味です。
解答を見る
fn main() { let rect = ((1, 2), (5, 5));
let width = rect.1.0 - rect.0.0; let height = rect.1.1 - rect.0.1;
println!("幅{width}、高さ{height}、面積{}", width * height);}実行結果:
幅4、高さ3、面積122つの点を一度に分解することもできます:let ((x1, y1), (x2, y2)) = rect;。書く量は増えますが、rect.1.0 - rect.0.0 より x2 - x1 のほうが読みやすいですね。こんなふうにドットが積み重なってきたら、名前付きのフィールドを持つ構造体にしたほうがわかりやすい、というサインです。
- タプルは、違う型の値もまとめられます:
(name, age, is_active)。 - 要素は
.0、.1、.2で取り出すか、分解で一度に取り出します。 - 関数から複数の値を返すには、タプルを使うのが自然です。
mutのタプルは要素を変更できますが、要素の数と型は変えられません。(a, b) = (b, a);で2つの値を入れ替えられます。- 名前つきで繰り返し使うまとまりには、構造体を使いましょう。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. 配列にはできなくて、タプルにはできることは何ですか?
2. person というタプルの2番目の要素を取り出すには、どう書きますか?
3. let mut a = 1; let mut b = 2; (a, b) = (b, a); の後、a と b はいくつですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- タプル型 — The Rust Programming Language 日本語版:タプルの作り方、分解、位置での取り出しを紹介する公式の入門です。
- タプル — Rust By Example 日本語版:入れ子のタプルや、タプルを関数の引数にする例を実行できます。
- The
tupletype — 標準ライブラリのドキュメント(英語):比較、コピー、{:?}での表示など、タプルが対応しているトレイトがわかります。 - The
unittype — 標準ライブラリのドキュメント(英語):()とは何か、どこに出てくるのかを説明しています。 - Destructuring assignments — The Rust Reference(英語):
(a, b) = (b, a);の背景にある正確なルールです。