Rustの配列、しっかり理解できていますか?
プログラミングではお馴染みの「配列」ですが、Rustの世界ではちょっとした個性を持っているんです。特に「サイズが固定」という点が、他の言語に慣れていると「おや?」と思うかもしれませんね。
この記事では、Rustの配列について、基本から解説していきます! 宣言の仕方、データの使い方、注意すべき点、そして相棒とも言える「Vec」(ベクタ)との違いまで、サンプルコードをたっぷり使いながら、分かりやすさ重視で進めていきます。
固定長って聞くとちょっと構えちゃうかもしれないけど、大丈夫!読み終わるころには、きっとRustの配列と仲良くなれているはずです。
この記事で学べること
- Rustの配列がどんなものか
- 配列の基本的な作り方(宣言と初期化)
- 配列の中のデータを取り出したり、変えたりする方法
- 配列全体をぐるっと見ていく方法(ループ処理)
- 配列を使うときに気をつけたいポイント
- よく似ている「Vec」(ベクタ)との違いと使い分け
さあ、Rust配列マスターへの第一歩を踏み出しましょう!
Rustの配列とは? ~固定サイズのデータ列~
まず、Rustの配列が何者なのか、その正体をつかみましょう。
Rustの配列は、同じ種類のデータを、決められた個数だけ、メモリ上に連続して並べておくための箱のようなものです。
ポイントは以下の3つです。
- サイズが固定
配列を作るときに「要素を何個入れるか」を決めたら、後から変えることはできません。5個と決めたら、ずっと5個のままです。これが一番の特徴ですね! - 同じ型の要素だけ
数値(例えば`i32`型)を入れると決めた配列には、数値しか入れられません。文字列(`String`型)や真偽値(`bool`型)など、他の種類のデータは混ぜられないルールです。 - メモリ上に連続して配置
データたちがメモリ上で隣り合ってきれいに整列しています。これは、コンピューターがデータにアクセスしやすく、処理が速くなる場合があるという利点につながることがあります。
他の言語だとサイズを自由に変えられる配列(のようなもの)が多いですが、Rustではサイズ固定の「配列」と、サイズ可変の「Vec」(ベクタ)が明確に区別されています。
この違いを意識するのが、Rustを使いこなすコツの一つですよ。
Rustの配列の基本的な書き方(宣言と初期化)
理屈がわかったところで、次は実際にコードを書いてみましょう!
Rustで配列を作る(宣言する)方法と、作った配列に最初の値を入れる(初期化する)方法を見ていきます。
型と要素数を指定して宣言 `[T; N]`
一番基本となる配列の作り方です。どのデータ型(Type)の要素を、何個(Number)入れるかを指定します。
書き方はこんな感じ。
// データ型と要素数だけ指定(この時点では中身は未定、またはデフォルト値) let a: [i32; 5]; // 宣言と同時に初期値も指定 let b: [i32; 3] = [10, 20, 30]; // i32型の数値を3つ持つ配列bを宣言し、初期値を入れる // 配列の中身を表示してみる println!("配列bの中身: {:?}", b);
上の例では、`b`という名前の配列を作っています。`[i32; 3]`の部分がキモで、「`i32`(32ビット整数)型のデータを」「`3`個格納できる」配列ですよ、と宣言しているわけですね。
そして、`= [10, 20, 30]`で、それぞれの場所に初期値を入れています。
データ型と要素数の指定は、この書き方では必須です。これがないとRustコンパイラがどんな配列を作ればいいか分かりませんからね。
実行結果はこうなります。
配列bの中身: [10, 20, 30]
初期値を与えて宣言・初期化
もっと簡単に書ける方法もあります。初期値を与えれば、Rustがある程度「いい感じ」に型や要素数を推測してくれるんですよ。
【パターン1】値を列挙する
初期値全部を `[]` の中に書いてしまう方法です。これだけで、型と要素数をRustが判断してくれます。
// 初期値を列挙するだけでOK let c = [1, 2, 3, 4, 5]; // これは [i32; 5] と推論されることが多い let d = ["apple", "banana"]; // これは [&str; 2] と推論される println!("配列cの中身: {:?}", c); println!("配列dの中身: {:?}", d);
実行結果
配列cの中身: [1, 2, 3, 4, 5] 配列dの中身: ["apple", "banana"]
【パターン2】値同じ初期値で埋める
配列の要素全部を、ひとまず同じ値で初期化したい場合に便利な書き方です。
// [初期値; 要素数] という書き方 let e = [0; 5]; // 全要素が0の、要素数5の配列 (型はi32と推論されることが多い) let f = [true; 3]; // 全要素がtrueの、要素数3の配列 (型はboolと推論される) println!("配列eの中身: {:?}", e); println!("配列fの中身: {:?}", f);
この `[初期値; 要素数]` という書き方は、同じ値で配列を埋めたい時に超便利なので、覚えておくと役立ちます。特に、最初は全部ゼロで初期化したい、みたいなケースで活躍します。
実行結果
配列eの中身: [0, 0, 0, 0, 0] 配列fの中身: [true, true, true]
Rustの配列の使い方(アクセスと反復処理)
配列を作れるようになったら、次はその中身を使ってみましょう!
特定の場所のデータを取り出したり、配列全体を順番に処理したりする方法を紹介します。
インデックスで要素にアクセス・変更
配列の中の特定のデータに用があるときは、「インデックス番号」を使います。インデックスは、配列の何番目の要素かを指定する番号のことです。
ここで超重要な注意点! インデックスは0から始まります! 3つの要素を持つ配列なら、インデックスは 0, 1, 2 となります。ここを間違えるとエラーのもとになるので気をつけてくださいね。
要素を読み取るのも、書き換えるのも `配列名[インデックス番号]` という形で行います。
// まずは配列を準備 let mut numbers = [10, 20, 30, 40, 50]; // 書き換える可能性があるので mut をつける // インデックスを使って要素を読み取る let first_number = numbers[0]; // 0番目の要素(最初の要素)を取得 let third_number = numbers[2]; // 2番目の要素(3番目の要素)を取得 println!("最初の要素: {}", first_number); // 結果: 10 println!("3番目の要素: {}", third_number); // 結果: 30 // インデックスを使って要素を変更する numbers[1] = 25; // 1番目の要素(2番目の要素)を25に変更 println!("変更後の配列: {:?}", numbers); // 結果: [10, 25, 30, 40, 50] // やってみよう!範囲外アクセス(これはエラーになります) // println!("{}", numbers[5]); // 存在しない5番目のインデックスにアクセスしようとするとパニック!
配列の中身を書き換える場合は、配列を宣言するときに `mut` を付けて「変更可能」にしておくのを忘れずに。
そして、最後のコメントアウト部分のように、配列の範囲を超えたインデックス(この例だと `numbers[5]`)にアクセスしようとすると、プログラムが「パニック」を起こして停止します。これはRustが安全を守るための仕組みなのです。
forループで全要素を処理
配列の要素を一つ一つ順番に見ていきたい、あるいは全部に同じ処理をしたい、という場面はよくありますよね。そんなときは `for` ループが便利です。
配列の全要素を順番に取り出して処理する、最も一般的な書き方を見てみましょう。
let fruits = ["リンゴ", "バナナ", "オレンジ"]; println!("フルーツ一覧:"); // forループで配列の各要素を順番に取り出す for fruit in fruits { // Rust 2021 edition以降はこの書き方がシンプル println!("- {}", fruit); } // 別の書き方(イテレータを使う) // for fruit in fruits.iter() { // println!("- {}", fruit); // }
このコードは、`fruits` 配列の中身を `fruit` という変数に一つずつ取り出し、`println!` で表示する、という処理を繰り返します。
実行結果
フルーツ一覧: - リンゴ - バナナ - オレンジ
`for` ループは、配列の中身をまとめてチェックしたり、処理したりする際の定番テクニックなので、ぜひマスターしておきましょう。
Rustの配列を使う上での注意点と比較
さて、配列の基本的な使い方が分かってきたところで、もう少しだけ知っておくと良い点と、よく似た機能である `Vec
固定サイズと境界外アクセスについて
繰り返しになりますが、Rustの配列は一度決めたらサイズ変更はできません。これが最大のルールであり、メリットでもデメリットでもある点です。
- メリット
サイズが変わらないことが保証されているので、プログラムの実行前にメモリのどこにどれだけ配置するかが決まりやすく、処理が速くなる可能性があります。また、サイズが常に一定なので、プログラムの挙動が予測しやすくなる面もあります。 - デメリット
プログラムを実行している途中で「やっぱり要素を増やしたいな」と思っても、配列ではできません。そういう場合は、次に説明するベクタを使う必要があります。
そして、もう一つ重要なのが「境界外アクセス」です。先ほども少し触れましたが、配列の範囲を超えたインデックス(例:要素数が3なのに `array[3]` にアクセスしようとすること)を指定すると、Rustはプログラムを安全のために停止(パニック)させます。
これは、意図しないメモリ領域を読み書きしてしまうような危険なバグを防ぐための、Rustの親切心(?)なのです。面倒に感じるかもしれませんが、安全なプログラムを書く上では非常に役立つ仕組みです。
ベクタ(`Vec<T>`)との違いと使い分け
Rustには配列とよく似た機能として `Vec
配列とベクタの主な違いを整理しましょう。
- サイズ
- 配列: 固定長(作るときに決めたサイズから変えられない)
- ベクタ: 可変長(実行中に要素を追加したり削除したりしてサイズを変えられる)
- メモリ確保場所(基本)
- 配列: スタック(比較的高速だけどサイズに制限あり)
- ベクタ: ヒープ(サイズ制限は緩やかだけど、管理コストが少し高い)
- 柔軟性
- 配列: 低い(サイズ変更不可)
- ベクタ: 高い(サイズ変更可能)
じゃあ、どっちを使えばいいの?という使い分けの目安はシンプルです。
- 配列を使う場面
- 最初から要素数が分かっていて、今後も変わらないことが確実な場合。
- パフォーマンスが非常にシビアで、少しでも速くしたい場合(ただし差は状況によります)。
- 組み込み開発など、メモリ管理に厳しい制約がある場合。
- ベクタを使う場面
- 実行時に要素数が変わる可能性がある場合。
- ファイルから読み込んだデータや、ユーザー入力など、事前に要素数が分からないデータを扱いたい場合。
- データを後から追加したり削除したりしたい場合 ← ほとんどのケースはこちらが便利!
迷ったら、まずは「後から要素を増やしたり減らしたりするかな?」と考えてみてください。その可能性があるなら、ベクタ `Vec
まとめ
お疲れ様でした!今回はRustの配列について、基本的なところから使い方、注意点、そしてベクタとの違いまでを見てきました。
最後に、今回の内容を簡単におさらいしておきましょう。
- Rustの配列は同じ型の要素を固定個数だけ格納するデータ構造。
- 宣言は `let a: [型; 個数] = [値...];` や `let b = [値1, 値2];` 、 `let c = [初期値; 個数];` のように書ける。
- 要素へのアクセスは `配列名[インデックス番号]` で行い、インデックスは0から始まる。
- `for` ループを使うと、配列の全要素を簡単に処理できる。
- 最大の注意点は固定長であること。サイズは後から変更できない。
- 範囲外のインデックスにアクセスするとパニックする(安全のための仕組み)。
- サイズを後から変えたい場合は、配列ではなくベクタ (`Vec<T>`) を使う。
どうでしょう?Rustの配列のイメージ、つかめてきましたか?
固定長という特徴はありますが、その性質を理解すれば、プログラムの意図を明確に示せる強力な武器になります。ぜひ、実際のコードで配列をどんどん使ってみてくださいね。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。