プログラミング

Rustで配列とベクターを使ってみる!

こんにちは、MSKです。

プログラムを書いていると、同じ種類のデータを複数個保存しておく必要があったり、通信などでデータをまとめて送る場合がよくあります。

例えば、1時間ごとの温度データや1クラス30人のテストの平均点などを保存しておくなどです。

2,3個程度なら、まだ良いと思いますが百個、千個と数が増えてくると1つずつ変数を定義して書いていくなんてやりたくないと思うのが普通です。

let temperature1 = 20.5;
temperature2 = 20.25;
temperature3 = 21.1;
・・・
temperature1024 = 20.1;

また、このように1つずつ変数を定義するとこれらの合計を求めたい、平均を求めたいなどまとめて扱おうとする時に大変です。

let mut sum = 0;
sum += temperature1;
sum += temperature2;
・・・
sum += temperature1024;
let ave = sum / 1024;

こんなのは書いてられないですよね。

こういう場合に使えるのが配列やベクターという機能です。
今回は配列とベクターに関しての基本的な内容を解説します。

過去の記事など

Rustのインストールなど過去の記事については以下になります。

Rustをインストールして使ってみよう!仕事で使っているチップも今まではC言語のみ対応でしたが、近いうちにRustにも対応するといったアナウンスがでるなど組み込み系の仕事でも使用されるようになってきています。 今日はそのRustについて特徴を紹介し、定番の「Hello World」を表示したいと思います。...

変数などについては以下で解説しています。

Rustで変数を使ってみよう!Rustで変数を使うことについて解説を行います。...

配列

まずは配列を使ってみます。
配列は入れるデータの個数を最初に決めます。
最初に決めた個数以上のデータを入れることはできません。

配列を使ってみる

配列を宣言するには変数を宣言するときと同じくletから始めます。
[と]の間に要素を1個以上書くことで配列を宣言します。

fn main() {
    let ary1 = [4, 3, 2, 1, 0];
    println!(
        "ary1 = [{},{},{},{},{}]\r\nary1 length : {}",
        ary1[0],
        ary1[1],
        ary1[2],
        ary1[3],
        ary1[4],
        ary1.len()
    );
}

ary1 = [4,3,2,1,0]
ary1 length : 5
と表示されたと思います。

上の例では整数を要素にとる長さ5の配列ary1を宣言しています。
各要素にアクセスするためには、(配列名)[アクセスしたインデックス]と書きます。
インデックスは0から始まり、長さがmであればm-1までの値をとります。
インデックスは配列に入力した順に0から割り当てられます。

※可変の配列(let mut)であれば、特定のインデックスを指定して紐づけられている値を変更することも可能です。
この後に解説します。

上の例の場合、例えばインデックス0に4が割り当てられています。
配列の長さを取得するためには、(配列名).len()を使用します。

配列では(長さ-1)以上のインデックスにアクセスしていません。
アクセスしようとすればどうなるのでしょうか?

println!の中身を次のように変えてbuildしてみます。

println!("ary1[5] = {}", ary1[5]);

buildすると次のようなエラーが出て失敗すると思います。

error: this operation will panic at runtime
 --> src/main.rs:3:30
  |
3 |     println!("ary1[5] = {}", ary1[5]);
  |                              ^^^^^^^ index out of bounds: the len is 5 but the index is 5
  |
  = note: `#[deny(unconditional_panic)]` on by default

error: aborting due to previous error

error: could not compile


C言語を使う場合などでよくあることですが、インデックスを変数により指定するコードを書いて、実行中に範囲外になり挙動がおかしくなって気づくということがあります。

そのような場合はRustではどうなるか見てみましょう。

fn main() {
    let ary1 = [4, 3, 2, 1, 0];
    let mut index = 0;
    println!("ary1[{}] = {}", index, ary1[index]);
    index = 5;
    println!("ary1[{}] = {}", index, ary1[index]);
}


cargo buildでbuildしてみると今度はbuildが成功します。
cargo runで走らせてみます。

ary1[0] = 4
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 5', src/main.rs:6:38

範囲外にアクセスしたときにパニックになり、プログラムが停止していることが分かります。
暴走しておかしい挙動などをしないので、バグの再現時などに原因特定しやすいと思います。

配列の型を指定する

配列の要素の型を明示的に指定することもできます。
let array : [型; 要素数] = [・・・]という形で宣言します。
型を指定した場合、必ず要素数を指定しなければなりません。

サンプルを見てみます。

fn main() {
    let ary2: [u8; 3] = [0x00, 0x90, 0xFF];
    println!("ary2[0] = 0x{:<02X}", ary2[0]);
    println!("ary2[1] = 0x{:<02X}", ary2[1]);
    println!("ary2[2] = 0x{:<02X}", ary2[2]);
}

8bitのデータが入っていることが分かります。

この配列に8bit以上の数値を入れようとするとエラーが発生します。
例えば、let ary2 : [u8;3] = [0x01,0x02,0x123];などとすればエラーになります。

配列の中身を変えてみる

通常の変数と同様にRustの配列もデフォルトでは中身を変更することができません。

変数と同じでletの後にmutをつけると配列の中身を変更することができるようになります。

fn main() {
    let mut ary3 = [1, 2, 3, 4, 5];
    println!("ary3[0] = {}", ary3[0]);
    ary3[0] = 100;
    println!("ary3[0] = {}", ary3[0]);
}

ary3[0] = 0
ary3[0] = 100
となっていたと思います。
配列の中身(この場合はインデックス0の要素)が変更できることが分かりました。

配列を初期化する

気温のデータを保存するなど、最初にはデータがなくて時間の経過と共にデータを入れていくようなことがあります。

そのようなときは例えば、保存する個数分0で初期化するということをよくやります。
配列の初期化は次のように[初期値;要素数]とします。

fn main() {
    let mut ary4: [u8; 3] = [0; 3];
    println!("ary4[0] = {}", ary4[0]);
    println!("ary4[1] = {}", ary4[1]);
    println!("ary4[2] = {}", ary4[2]);
    ary4[0] = 1;
    ary4[1] = 2;
    ary4[2] = 3;
    println!("ary4[0] = {}", ary4[0]);
    println!("ary4[1] = {}", ary4[1]);
    println!("ary4[2] = {}", ary4[2]);
}

ベクター

次はベクターを使ってみます。
配列では入れることのできる要素の数は最初に決めた値で固定でした。
この要素数を可変にできるのがベクターです。

ベクターを使ってみよう

ベクターを使うにはletで変数を宣言するのは同じなのですが、配列の[]の前にvec!と書く必要があります。

サンプルを見てみます。

fn main() {
let v1 = vec![4, 3, 2, 1, 0];
    println!(
        "v1 = [{},{},{},{},{}]\r\nv1 length : {}",
        v1[0],
        v1[1],
        v1[2],
        v1[3],
        v1[4],
        v1.len()
    );
}

vec!を使う以外は配列とほとんど変わらないように使えます。
要素数以上のインデックスでアクセスしようとすると配列と同じようにエラーが起こります。

ベクターの要素を増やしてみよう

ベクターの要素を増やすにはpushを使います。
(ベクターの変数名).push(ベクターに追加したデータ)とします。

ベクターの型以外を入れようとするとエラーになります。

サンプルを見てみます。

fn main() {
    let mut v2 = vec![1, 2, 3];
    println!("v2 len = {}", v2.len());
    v2.push(4);
    v2.push(5);
    //以下はエラーになる
    //v2.push(0.3);
    //v2.push("abc");
    println!("v2 len = {}", v2.len());
    println!("v2 = [{},{},{},{},{}]", v2[0], v2[1], v2[2], v2[3], v2[4]);
}

最初の時点では要素数は3ですが、pushした後では5個に増えているのが分かります。
また、v2の中身をみるとpushした値が末尾に追加されていることも確認できます。

ベクターの要素を減らしてみよう

ベクターの要素を削除する方法は2つあります。

1つ目は末尾から要素を取得して削除する方法です。
(ベクターの変数名).pop()と書きます。

このとき、末尾からデータが返されます。
返されるデータは値を含んだオプション型です。

fn main() {
    let mut v3 = vec![1, 2, 3];
    println!("v3 len = {}", v3.len());
    println!("v3.pop() = {:?}", v3.pop());
    println!("v3.pop() = {:?}", v3.pop());
    println!("v3.pop() = {:?}", v3.pop());
    println!("v3 len = {}", v3.len());
}

もう1つは消したいインデックスを指定できるremoveを使うことです。

let mut v4 = vec![1, 2, 3, 4];
println!("v4[2] = {}", v4[2]);
println!("v4 len = {}", v4.len());
println!("v4.remove(2) = {}", v4.remove(2));
println!("v4[2] = {}", v4[2]);
println!("v4 len = {}", v4.len());

最初のv4のインデックス2のデータには「3」が入っていますが、removeをした後では「4」が入っていて、インデックス2に入っていた「3」が削除されていることが分かります。

また、remove前のベクターの要素数は「4」が返されますが、remove後では「3」が返され要素数も減っていることが分かります。

最後に

今回は配列とベクターの基本について解説しました。
配列とベクターの基本についてまとめると、

  • 配列もベクターも同じ種類のデータを複数まとめて保存しておくもの
  • 配列は宣言時点で要素数が決められて、その中でしか使えない
  • ベクターは要素数が可変

です。

最後までご覧いただき、ありがとうございました。

「Rustで配列とベクターを使ってみる!」でした。

ABOUT ME
MSK
九州在住の組み込み系エンジニアです。 2児の父親でもあります。 数学やプログラミングが趣味です。 最近RustとReact、結び目理論と曲面結び目理論にはまっています。