導入
所有権のルールは、変数間の代入だけでなく関数呼び出しでもまったく同じように働きます。String を関数に渡すと、所有権が関数の中へムーブします。ここを理解すると、なぜ次章の「借用」が必要になるのかが腑に落ちます。
説明
String を関数に渡してみます。
fn main() {
let s = String::from("hello");
take_ownership(s); // s の所有権が関数へムーブする
// println!("{s}"); // ← エラー! s はもう main のものではない
}
fn take_ownership(text: String) {
println!("受け取った: {text}");
} // ← ここで text がスコープを抜け、ヒープが解放される
take_ownership(s)で、sの所有権は引数textへムーブします。mainのsは無効になります。- 関数が終わると
textがスコープを抜け、ヒープが解放されます。だからmainに戻ってきたとき、sはもう指す先がなく、使えません。
渡した値を呼び出し側でも使い続けたいなら、所有権を返してもらう必要があります。
fn main() {
let s = String::from("hello");
let s = give_back(s); // 所有権を渡し、戻り値で受け取り直す
println!("戻ってきた: {s}"); // OK
}
fn give_back(text: String) -> String {
println!("処理中: {text}");
text // 所有権を呼び出し側へ返す
}
「渡す → 返してもらう」で使い続けられますが、毎回これを書くのは明らかに面倒です。値を使わせてもらうだけ(所有権は渡さない)で済ませたい——この自然な要求に応えるのが、次章の 参照と借用 です。関数に &s と「参照」を渡せば、所有権はムーブせず、呼び出し側が持ったまま関数に中身を見せられます。
graph TD
a["main: s が所有"] -->|"take_ownership(s)"| b["関数へ所有権がムーブ"]
b --> c["関数終了で drop<br/>main の s は無効"]
a2["main: s が所有"] -->|"borrow(&s)"| d["関数は借りるだけ<br/>所有権は main のまま"]
d --> e["関数終了。s はまだ使える"]
試すには
上の1つ目のコードで take_ownership(s); のあとに println!("{s}") を足すとエラーになります。これを「毎回返り値で返す」以外の方法で解決するのが借用です。次章で take_ownership(&s) に変えるだけで、s を使い続けられることを確認します。