Chapter 11

タプル

タプル

タプルは,複数の変数をまとめて一つの変数として扱えるようにしたものです.

fn main() {
    let tuple: (i32, f64, i32) = (10, 2.5, 20);
    println!("1 番目の要素:{}", tuple.0);
    println!("2 番目の要素:{}", tuple.1);
    println!("3 番目の要素:{}", tuple.2);
}

tuple という変数があり,型は (i32, f64, i32) ,代入された値は (10, 2.5, 20) となっています.

こうすると, tuple.0tuple.1tuple.2 という 3 つの変数が使えるようになります.数え方が .1 .2 .3 ……ではなく .0 .1 .2 ……であることに注意してください.

tuple.0 tuple.1 tuple.2 ……の型は,それぞれ括弧の中の 1 番目, 2 番目, 3 番目……の型になります.今回は tuple の型が (i32, f64, i32) なので, tuple.0 の型が i32 で, tuple.1 の型が f64 で, tuple.2 の型が i32 です.

tuple.0 tuple.1 tuple.2 ……の値も,それぞれ括弧の中の 1 番目, 2 番目, 3 番目……の値になります.今回は tuple の値が (10, 2.5, 20) なので, tuple.0 の値が 10 で, tuple.1 の値が 2.5 で, tuple.2 の値が 20 です.

よって,出力は 1 番目の要素:10 2 番目の要素: 2.5 3 番目の要素:20 となります.

(10, 2.5, 20) は,最後にもう一つカンマを付けて (10, 2.5, 20,) としても同じです.

ここで問題です.i32 型の変数は 2^{32} 通り, i64 型の変数は 2^{64} 通りの値をとることができます.では, (i32, i64) 型の変数は何通りの値をとることができるでしょうか?

答え

2^{32} \times 2^{64} = \bm{2^{96}} 通り
i32i64 のとりうる値の集合をそれぞれ XY とすると,(i32, i64) は直積 X \times Y に相当します.

パターンマッチ

let を使って変数を宣言するとき,今まで次のような書き方をしていました.

let 変数名:=;

実は, let の後には,単なる変数名だけでなく,パターンを書くことができます.

タプルの形をしたタプルパターンもその一つです.

fn main() {
    let tuple = (10, 2.5);
    let (x, y) = tuple;
    assert_eq!(x, 10);
    assert_eq!(y, 2.5);
}

let の後に,タプルと同じ形式で (x, y) と書かれています.こう書くと, = の右辺のタプルの要素が,それぞれ xy に代入されます.これをパターンマッチといいます.

今回は 1 つめの 10 が x に, 2 つめの 2.5 が y にそれぞれ代入されるので,続く 2 つの assert_eq! は両方成功して,プログラムは正常終了します.

コードの中で,「x の値が 10, y の値が 2.5 になっている」ことを説明するために assert_eq! マクロを使いました.この本では,このように説明としてアサートを使うことがあります.登場したら,「アサートの条件が成り立っていて,実行すると正常終了する」という意味で読んでください.

パターンの形式が右辺と合っていないようなものは,

fn main() {
    let (x, y) = 10;
    let (p, q, r) = (10, 20);
}

エラーになります.

error[E0308]: mismatched types
 --> src/main.rs:2:9
  |
2 |     let (x, y) = 10i32;
  |         ^^^^^^   ----- this expression has type `i32`
  |         |
  |         expected `i32`, found tuple
  |
  = note: expected type `i32`
            found tuple `(_, _)`

error[E0308]: mismatched types
 --> src/main.rs:3:9
  |
3 |     let (p, q, r) = (10i32, 20i32);
  |         ^^^^^^^^^   -------------- this expression has type `(i32, i32)`
  |         |
  |         expected a tuple with 2 elements, found one with 3 elements
  |
  = note: expected tuple `(i32, i32)`
             found tuple `(_, _, _)`

mismatched types は,「型が合っていない」という意味です. this expression has type `i32` は「この式は i32 型である」, expected `i32`, found tuple は「i32 が来るはずだったが,実際にはタプルがあった」という意味です.expected a tuple with 2 elements, found one with 3 elements は,「要素が 2 つのタプルが来るはずだったが,実際には要素が 3 つのタプルだった」という意味です.

型注釈を付けるときは次のようになります.

fn main() {
    let (c, g): (i32, f64) = (299792458, 6.67430e-11);
    assert_eq!(c, 299792458_i32);
    assert_eq!(g, 6.67430e-11_f64);
}

タプルを返すブロック

次のコードを見てみましょう.

use proconio::input;

fn main() {
    input! {
        a: i32,
        b: i32,
    }
    let (max, min) = if a > b { (a, b) } else { (b, a) };
    assert!(max >= min);
    println!("大きい方:{}", max);
    println!("小さい方:{}", min);
}

まず標準入力で 2 つの数 ab を受け取っています.次の if 式が何を返しているか見てください. a > b のときはタプル (a, b) を返し, a \leq b のときはタプル (b, a) を返しています.そして返されたタプルを,パターンマッチを使って (max, min) で受け取っています.よって, max の値は ab のうち大きい方, min の値は小さい方となります.

このように,タプルを使うことで,ブロックが 2 つの値を同時に返すことができます.

ユニット

ブロックの最後に式を書くことで,値を返すことができるのでした.

実は,ブロックの最後に式を書かないときもある値が返されています.それは,要素が 0 個のタプル () です.

fn main() {
    let unit: ();
    unit = {
        println!("() を返すブロック");
    };
    assert_eq!(unit, ());
}

括弧の中が空なので,型も () で値も () です.要素が無いため, () 型の変数は, () という 1 通りの値を取ることしかできません.