はじめに
Rust の特徴である 所有権( ownership ) と 借用( borrowing ) を理解するために、値渡しと参照渡しのサンプルを見ていきます。
不変( immutable )と可変( mutable )の両方を比較して理解しましょう。
不変
【値渡し】
fn main() {
let s = String::from("hello");
// s をそのまま渡すと「所有権」が移動してしまう
print_str(s);
// ↓ ここではもう s は使えない
// println!("{}", s); // エラー
}
fn print_str(text: String) {
println!("{}", text);
}
ポイント
- String はヒープにデータを持つ型 で、値渡しすると所有権が関数に移動する
- 移動した後の
sはもう使えない
【参照渡し】
fn main() {
let s = String::from("hello");
// & をつけると参照渡しになる(所有権は移動しない)
print_str(&s);
// s はまだ使える
println!("{}", s);
}
fn print_str(text: &String) {
println!("{}", text);
}
ポイント
-
&sは読み取り専用の参照 - 関数内で
textを変更することはできない - 所有権は移動しないので、main で
sを使える
可変
値渡し
fn main() {
let mut s = String::from("hello");
// 所有権が移動するので main の s はもう使えない
append_world(s);
// println!("{}", s); // エラー!s の所有権は関数に移動した
}
fn append_world(mut text: String) {
text.push_str(" world");
println!("関数内: {}", text);
}
ポイント
- 可変でも値渡しすると所有権が移動する
- 関数内で変更できても、呼び出し元には反映されない
参照渡し
fn main() {
let mut s = String::from("hello");
// 可変参照を渡す
append_world(&mut s);
// main の s も変更されている
println!("main: {}", s);
}
fn append_world(text: &mut String) {
text.push_str(" world");
println!("関数内: {}", text);
}
ポイント
-
&mut sは可変参照 - 関数内で変更すると呼び出し元にも反映される
- 所有権は移動しない
借用ルールまとめ
| 値 | 参照 | 可変か |
|---|---|---|
| s | なし | 所有権を持つ |
| &s | 不変参照 | 読み取り専用、複数同時に持てる |
| &mut s | 可変参照 | 書き込み可能、同じスコープで1つしか持てない |
ポイント:
- 値渡しは所有権が移動する
- 不変参照は複数同時に持てる
- 可変参照は1つしか持てない
- 参照は借用( ownership は移動しない)
型と所有権
fn main() {
let s1: String = String::from("hello"); // 所有権を持つ
let s2: &str = "hello"; // 借用、所有権はない
}
-
Stringはヒープ上にデータを持つため所有権の概念が重要 -
&strは静的な文字列スライスで所有権は持たない
まとめ
- 値渡し → 所有権が移動する
-
&→ 不変参照、所有権は移動しない -
&mut→ 可変参照、所有権は移動せず変更可能、1つしか持てない - 可変と不変で借用ルールが異なることに注意