はじめに
変数には 1 つの値を格納します。一方、多くのプログラムでは、長さを変更できる順序付きリストが必要です。Rust の Vec<T> は、要素を追加できるシーケンスです。すべての要素は型 T を持ち、値の順序が保たれ、ベクターが要素を所有します。
この実験では、カフェの注文リストを作成し、特定の位置を安全に読み取り、リストを消費せずにすべての注文を確認し、可変参照を使って格納されたすべての文字列を更新します。
ベクターを作成して要素を追加する
このステップでは、最初は空のベクターに、所有権を持つ文字列を 3 つ追加します。
用意されたプロジェクトに移動し、ソースファイルを開きます。
cd /home/labex/project/cafe-orders
nano src/main.rs
Vec<String> は「要素が所有権を持つ String 値であるベクター」を意味します。これは、Option<u32> で見たものと同じ、コンテナの型を指定する山括弧の記法です。ここでは、格納される要素の型が String です。このような再利用可能な型の定義方法については、後のジェネリクスの Lab で学びます。Vec::new() は空のベクターを作成します。要素を追加するとベクターが変更されるため、束縛には mut が必要です。
Step 1 のコメントを、次の 3 つの push 呼び出しに置き換えます。
orders.push(String::from("Latte"));
orders.push(String::from("Tea"));
orders.push(String::from("Cake"));
push は要素を末尾に追加し、追加した順序を維持します。Ctrl+O で保存し、Enter キーを押してから、Ctrl+X で終了します。続けて、チェックと実行を行います。
cargo check
cargo run --quiet
出力は次のようになります。
Order count: 3
Orders: ["Latte", "Tea", "Cake"]
len() は要素数として 3 を返します。:? プレースホルダーは Rust のデバッグ形式を使用して、文字列を囲む引用符も含め、ベクター全体を表示します。
位置を安全に読み取る
このステップでは、範囲外アクセスによるパニックを発生させずに、ベクターの 2 番目の位置を読み取ります。
ベクターの位置は 0 から始まるため、インデックス 1 は 2 番目の注文を示します。get メソッドは Option<&String> を返します。指定した位置が存在する場合、Some に共有参照が格納され、位置が存在しない場合は None になります。この参照を使うと、ベクターが要素の所有者であり続けたまま、要素を読み取れます。
ソースファイルを開きます。
nano src/main.rs
Step 2 のコメントを、次のコードに置き換えます。
match orders.get(1) {
Some(order) => println!("Second order: {order}"),
None => println!("Second order: missing"),
}
このコードは、ベクターから String を取り出して所有権を移動させずに、要素を読み取ります。保存して終了したら、次のコマンドを実行します。
cargo run --quiet
出力の最後に、次の行が追加されます。
Second order: Tea
この行から、位置 1 が存在し、追加した順序も維持されていることを確認できます。
共有参照を使って反復処理する
このステップでは、後でベクターを使用できる状態を保ったまま、すべての注文を順番に確認します。
&orders と書くと、ベクターを for ループに移動する代わりに借用できます。そのため、ループ内の各値は、格納された 1 つの String への共有参照になります。
ソースファイルを開きます。
nano src/main.rs
Step 3 のコメントを、次のループと後続の確認コードに置き換えます。
for order in &orders {
println!("Queued: {order}");
}
println!("Stored after display: {}", orders.len());
保存して終了したら、次のコマンドを実行します。
cargo run --quiet
追加された行は次のようになります。
Queued: Latte
Queued: Tea
Queued: Cake
Stored after display: 3
ループがベクターを借用し、各共有参照を次の反復に進む前に返しているため、ループの後でも最後の要素数を使用できます。
可変参照を使って更新する
このステップでは、各ベクター要素を可変で借用し、表示ループが実行される前に、その場で更新します。
1 つの値に対して、同時に有効な可変参照は 1 つだけです。&mut orders に対する for ループは、1 回の反復で 1 つの要素への可変参照を取り出し、その反復で使用してから、次の要素へ進むことで、このルールを守ります。
ソースファイルを開きます。
nano src/main.rs
Step 4 のコメントを、次のコードに置き換えます。
for order in &mut orders {
order.push_str(" - ready");
}
push_str は、借用したテキストを所有権を持つ各 String の末尾に追加します。Rust はこのメソッド呼び出しで、各参照の背後にある可変な String を自動的に使用します。
保存して終了したら、チェックと実行を行います。
cargo check
cargo run --quiet
完全な出力は次のようになります。
Order count: 3
Orders: ["Latte", "Tea", "Cake"]
Second order: Tea
Queued: Latte - ready
Queued: Tea - ready
Queued: Cake - ready
Stored after display: 3
更新されたキューの行から、すべての要素がその場で変更されたことを確認できます。最後の要素数から、ベクターが同じ 3 つの文字列を引き続き所有していることを確認できます。
まとめ
Vec<String> を作成して要素を追加し、get を使って位置を安全に読み取り、ベクターを消費せずに反復処理し、可変参照を通してすべての要素を更新しました。


