所有権の「参照」「借用」について

所有権の「参照」「借用」について

所有権のクローンは別のアドレスに複製を作りましたが、元の変数と同じアドレスを持ちながら元の変数から別の変数や引数に値を借りることもできます。そのためには、変数や引数を宣言する際に、元の変数のアドレスを「参照」する「&」を付けることで値を借りることができます。関数の引数に参照を取ることを「借用」と言います。

例えば、次のサンプルコードのように「good1」変数の前で「&」を付けて参照すれば、代入した後でも「good1」変数に所有権が残るため「main()」関数のスコープ内で「good1」変数が使えます。

・所有権の参照のサンプルコード
fn main() {
    let good1 = String::from("Good");
    let _good2 = &good1;
    println!("{}",good1); // エラーなし
}

【サンプルコードの解説】
「good1」変数に"Good"という文字列を代入して宣言します。
「_good2」変数に「good1」変数を参照したアドレスを代入して宣言します。
「good1」変数の文字列をターミナルに表示します。

所有権の「参照」「借用」「関数」について

関数の引数で借用するには、変数の前で参照することを意味する「&」を付け、関数宣言時に「引数の型」の前でも参照することを意味する「&」を付けます。

図7とサンプルコードのように借用することにより、「good_str」関数内のスコープで引数が所有権を持たないため所有権がこのスコープで解放されることはなく、元の「good1」変数にはまだ所有権が残ります。

図7:所有権の「参照」「借用」「関数」

・所有権の参照・借用・関数のサンプルコード
fn main() {
    let good1 = String::from("Good");
    good_str(&good1);
    println!("{}",good1); // エラーなし
}

fn good_str(good: &String) {
    println!("{}",good); // エラーなし
}

【サンプルコードの解説】
「good1」変数に"Good"という文字列を代入して宣言します。
「good_str」関数に「&good1」という風に変数の前に「&」を付け、関数の引数の「good: &String」という風に型の前に「&」を付けて参照できます。
「good」引数の文字列をターミナルに表示します。
「good1」変数の文字列をターミナルに表示します。

所有権の「参照」「借用」「関数」「ミュータブル」について

関数の引数で借用して関数内で値を変更する場合には、図8とサンプルコードのように変数の前で参照する「&mut」を付け、引数の型の前で参照する「&mut」を付けます。

また、ミュータブルな変数をイミュータブルに参照した後にミュータブルに変数を参照したり、ミュータブルな参照が複数あるとエラーになります。

図8:所有権の「参照」「借用」「関数」「ミュータブル」

・所有権の参照・借用・関数・ミュータブルのサンプルコード
fn main() {
  let mut good1 = String::from("Good");
  good_str(&mut good1);
  println!("{}",good1); // エラーなし
}

fn good_str(good: &mut String) {
  good.push('y');
}

【サンプルコードの解説】
「good1」変数に"Good"という文字列を代入してミュータブル(mut)で宣言します。
「good_str」関数に引数としてミュータブルな「good1」変数を参照して渡します。
「good」引数の後ろに"y"文字を足して"Goody"という文字列に変更します。
「good_str」関数内で変更された「good1」変数の文字列"Goody"をターミナルに表示します。

for文におけるイテレータについて

「for文」とは第3回でも解説した通り、決まった回数だけループする繰り返し処理の「制御構文」のことです。そのfor文で値を借用するには「iter()」メソッドで「イテレータ」を使います。そうすれば「v1」vec!マクロの各要素を参照するだけで、所有権はムーブされません。

次のサンプルコードのように、for文でコレクションは所有権を所有せず一時的に借りるだけで、ループ後もコレクションを再利用できます。「vec!」マクロに「iter()」関数を付けけないと所有権がfor文のスコープを出るときに解放されてしまい、ループ後に再利用できません。

・for文とイテレータのサンプルコード
fn main() {
    let v1 = vec!["A","B","C"];
    for v2 in v1.iter() {
        println!("{}",v2);
    }
    println!("{:?}",v1); // エラーなし
}

【サンプルコードの解説】
vec!マクロの「v1」配列に"A"と"B"と"C"の文字列を代入して宣言します。
「for v2 in v1.iter()」で「v2」変数に"A"と"B"と"C"の値が順番に代入され「{}」内をループ処理します。
「iter(イテレータ)」はループごとにコレクションの要素を借用します。
「v1」配列の文字列をターミナルに表示します。

スライス型について

所有権のない別のデータ型に「スライス型」があります。文字列や配列やタプルなどの複数の要素を持つ値の中の一部、または全部を参照することもできます。例えば"Good"文字列の0~3番目未満の"Goo"文字列だけ取得する感じです。

図9とサンプルコードのように文字列変数の前に「&」を付け、変数の後に「[」と「]」で囲って要素の何番目から何番目未満までの要素か「..」で範囲を指定します。

図9:「スライス型」

・スライス型のサンプルコード
fn main() {
    let good1 = String::from("Good");
    let good2 = &good1[0..3];
    println!("{}",good1); // エラーなし
    println!("{}",good2); // エラーなし
}

【サンプルコードの解説】
「good1」変数に"Good"という文字列を代入して宣言します。
「good2」変数に"Good"という文字列の0~3未満の文字列"Goo"を代入して宣言します。
「good1」変数の文字列をターミナルに表示します。
「good2」変数の文字列をターミナルに表示します。

おわりに

今回は変数に所有権を与え、所有権の「ムーブ」「クローン」「コピー」「参照」「借用」の5大要素と、「所有権と関数」「イテレータでの借用」「スライス型」について解説しました。

たとえ詳細に所有権を解説しても、Rustのコードを書いていれば誰でもエラーが出ることはあるでしょうから、その度にエラーメッセージを見てデバッグしていくうちに体で所有権を理解した方が良いと思います。

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored

ClickHouse Cloudが実現した広告運用SaaS「ATOM」のデータ基盤革新、パフォーマンスを向上しながら、運用負荷とコストを削減 11月11日 6:20 AIで開発が加速する時代、品質保証はどう変わる? Tricentisが示すAIエージェント活用の実像 11月9日 6:45 悪用までの猶予が「1分」に迫るフロンティアAI時代に開発と本番を貫く脆弱性管理が必要 10月29日 8:30 欧州CRA対応とSBOM運用。AI生成コードのOSS混入問題の対策とは ――スニペット単位で検出する「FossID」「Insignary Clarity」によるSBOM運用 9月16日 6:40 PostgreSQLからTiDBへ、稼働中SaaSで進める段階的DB移行の実践と再設計への展望 9月7日 6:30 機能安全開発におけるGoogleTestの活用を支援!「Parasoft C/C++test CT」で実現する効率化&AI連携 4月14日 6:30 【CNDW2025】Grafanaが明かす「オブザーバビリティの哲学」ー最小限の労力で実用的なインサイトを得るには 1月23日 6:30 【CNDW2025】プロダクト急増に備える基盤刷新 ーウェルスナビがECSからEKSへの移行で得た知見とは 1月15日 6:30 【CNDW2025】250環境を5人で運用、構築時間は30分に ーKINTOテクノロジーズが語るインフラ基盤組織の作り方 2025年12月18日 6:30 新たな自動化で熱視線! AIエージェントの「推論能力」を支える2つのコンポーネントとは? 2025年11月28日 6:30