コンテンツにスキップ

参照

参照とは、ある値へのポインタを保持することである。 そのポインタを利用して、値を書き換えたり読み取ったりできる。

Rustでは参照周りのルールが複雑だが、それは危ない操作を防ぐために必要なものである。

データ競合が発生しうるコードのコンパイルを防ぐため、参照にはいくつかの制約がある。

具体的には以下の3つの条件を同時に満たす場合にデータ競合が発生しうる。

  • 2つ以上のポインタが同じデータに同時にアクセスする。
  • 少なくとも1つのポインタがデータに書き込みを行っている。
  • データへのアクセスを動悸する機構が使用されていない。

ダウンリングポインタとは、他人に渡されてしまった可能性のあるメモリを指すポインタのことである。 その箇所へのポインタを保持している間に、メモリを開放してしまうことで発生する。

Rustでは、ダウンリングポインタが発生しないように設計されているため、ポインタが指す値が必ず存在することが保証される。

実装でお気持ちを実際に確認してみる

同じスコープで同時に2つの可変参照を持つことはできない。

main.rs
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // E0499
println!("{}, {}", r1, r2);

可変参照と普遍参照が同時に存在することの禁止

Section titled “可変参照と普遍参照が同時に存在することの禁止”

同じスコープで可変参照と普遍参照を同時に持つことはできない。

main.rs
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s; // 不変参照は同時に存在してOK
let r3 = &mut s; // E0502
println!("{}, {}", r1, r2);

関数を使った例

main.rs
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // E0502
println!("The first word is {}", word);
}

first_word()関数はsを不変で借用している。 そのあとのs.clear()では、sを可変参照しようとしているため、エラーになる。

clear()の実装:

str.rs
pub fn clear(&mut self) {
self.vec.clear()
}
main.rs
fn main() {
let reference_to_nothing = dangle();
}
// E0106
fn dangle() -> &String {
let s = String::from("hello");
&s
}

E0106のエラーメッセージは、ライフタイムに関するものであり、まだ知らない機能である。

Stringを直接返すことで解決する:

main.rs
fn no_dangle() -> String {
let s = String::from("hello");
s
}

所有権がMoveされるので、関数を抜けたあとでも問題なく変数s(から所有権が移ったもの)を扱える。