コンテンツにスキップ

Rustの配列

配列(array)は、同じ型の値を決まった数だけ並べたものです。大事なのは「決まった数」というところです。配列は一度作ると、あとから大きくしたり小さくしたりできません。サイズを変えたいときに使うのはベクタです。

fn main() {
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
println!("{:?}", numbers);
}

[i32; 5] という型は「i32 の値が5個入った配列」と読みます。この長さは型の一部です。つまり、[i32; 5] と [i32; 3] は別の型として扱われます。

▶ 実際に試してみよう

型はRustに推論してもらい、値だけを書くこともできます。

let numbers = [1, 2, 3, 4, 5];

同じ値をN個並べた配列を作るには、次の短い書き方を使います。

fn main() {
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
println!("{:?}", zeros);
}
▶ 実際に試してみよう

角かっこを使います。番号(インデックス)は0から始まります。

fn main() {
let fruits = ["りんご", "バナナ", "さくらんぼ"];
println!("{}", fruits[0]); // りんご
println!("{}", fruits[2]); // さくらんぼ
println!("要素数: {}", fruits.len());
}
▶ 実際に試してみよう

範囲外アクセスはきちんと止められる

Section titled “範囲外アクセスはきちんと止められる”

存在しないインデックスにアクセスしようとすると、Rustはでたらめなメモリをこっそり読むのではなく、プログラムを止めてくれます。

fn get_fruit(fruits: [&str; 3], index: usize) -> &str {
fruits[index]
}
fn main() {
let fruits = ["りんご", "バナナ", "さくらんぼ"];
println!("{}", get_fruit(fruits, 10)); // 実行時にパニックする
}
thread 'main' (12345) panicked at src/main.rs:2:5:
index out of bounds: the len is 3 but the index is 10

このメッセージは「範囲外のインデックスです。長さは3ですが、インデックスは10です」という意味です。Rust自身が出すメッセージなので、英語のままになります。

fn main() {
let numbers = [10, 20, 30];
for n in numbers {
println!("{n}");
}
}
▶ 実際に試してみよう

配列の中には、別の配列を入れられます。そうすると、ゲームの盤面や小さな表計算シートのような、行と列を持つグリッドが作れます。下の grid の型は [[i32; 3]; 2] で、「i32 が3個入った配列が2行」と読みます。

fn main() {
// 2行3列
let grid = [
[1, 2, 3],
[4, 5, 6],
];
println!("1行目・2列目: {}", grid[1][2]);
let mut total = 0;
for row in grid {
println!("{:?}", row);
for value in row {
total += value;
}
}
println!("合計: {total}");
}

実行結果:

1行目・2列目: 6
[1, 2, 3]
[4, 5, 6]
合計: 21
▶ 実際に試してみよう

grid[1][2] は「1行目、そしてその行の中の2列目」という意味です。どちらも0から数えるので、これは2番目の行の3番目の値です。すべての値をたどるには、行をまわすループを1つ書き、その中に各行の値をまわすループをもう1つ書きます。

行の長さは型の一部なので、どの行も同じ長さでなければなりません。ループしながら位置も知りたいときは、.iter().enumerate() を使うとインデックスと値を一緒に受け取れます。これはforのページで紹介しています。

サイズが決まっていて、最初からわかっているとき(1週間の7日など)は配列を使います。要素を追加したり削除したりする必要があるときは Vec を使います。実際のところ、ほとんどの場面は後者です。普段のRustのコードでは、配列はベクタほどよくは使われません。

配列 [4, 8, 15, 16, 23, 42] を作ります。すべての値を足し合わせ、合計と、小数点以下2桁までの平均を表示してください。ヒント:for でループして total に足していき、割り算の前に as で両方の数を f64 に変換します。

解答を見る
fn main() {
let numbers = [4, 8, 15, 16, 23, 42];
let mut total = 0;
for n in numbers {
total += n;
}
let average = total as f64 / numbers.len() as f64;
println!("合計: {total}");
println!("平均: {average:.2}");
}

実行結果:

合計: 108
平均: 18.00
▶ 実際に試してみよう

as f64 がないと、total / numbers.len() は整数の割り算になってしまいます。それに、total(i32)と len()(usize)は型が違う、とRustに怒られてしまいます。

1週間の気温 [18, 25, 12, 31, 22, 27, 19] があります。いちばん高い気温を見つけて表示してください。ヒント:let mut hottest = temps[0]; から始めて、ループしながら、それより大きい値が見つかるたびに置き換えます。

解答を見る
fn main() {
let temps = [18, 25, 12, 31, 22, 27, 19];
let mut hottest = temps[0];
for t in temps {
if t > hottest {
hottest = t;
}
}
println!("最高気温: {hottest}");
}

実行結果:

最高気温: 31
▶ 実際に試してみよう

0 ではなく temps[0] から始めるのが大事です。もし気温がすべて0度未満だったら、0 から始めると間違った答えになってしまいます。

  • 配列の長さは固定で、型の一部になっています: [i32; 5]。
  • 要素には array[index] でアクセスします。番号は0から始まります。
  • 範囲外アクセスは、未定義動作になるのではなく、実行時にパニックします。
  • 配列には配列を入れられます。[[i32; 3]; 2] はグリッドで、grid[行][列] で読み出します。
  • サイズを変えたいなら、代わりに Vec を使いましょう。

理解度チェック

1. Rustの配列の長さについて正しいものはどれですか?

2. 実行時に配列の範囲外のインデックスにアクセスすると、どうなりますか?

3. let zeros = [0; 5]; は何を作りますか?

スコア: 0 / 3