RustのRc
1. Rcの基本的な概念
Rustでは、所有権を持つのは1つの変数だけであり、他の変数がそのデータを借用して使うことはできますが、所有権を移動させることなく複数の所有者を持つことはできません。この制約を緩和するために、Rc
という型が登場します。

Rc
は、参照カウント型のスマートポインタです。これを使うことで、複数の所有者が同じデータを共有し、各所有者がそのデータを参照できるようになります。Rc
は、所有権を「共有」するために、内部で参照カウントを保持します。このカウントが0になると、メモリが自動的に解放されます。
2. Rcの使い方
Rc
を使うためには、まずstd::rc::Rc
をインポートする必要があります。以下のコードは、基本的な使い方の例です。
rustuse std::rc::Rc;
fn main() {
let x = Rc::new(5); // Rcでデータをラップ
let y = Rc::clone(&x); // Rcの複製を作成
println!("x: {}, y: {}", x, y); // xとyは同じデータを参照している
println!("参照カウント: {}", Rc::strong_count(&x)); // 現在の参照カウントを表示
}
この例では、Rc::new
を使って整数5をラップしたRc
型のポインタを作成しています。その後、Rc::clone
を使ってx
の参照カウントを増やすための複製を作成し、y
もx
が指しているデータを参照するようになります。
出力は次のようになります。
makefilex: 5, y: 5
参照カウント: 2
ここで、Rc::strong_count(&x)
を使って、参照カウントが2であることを確認できます。x
とy
は共に同じデータを参照しており、そのため参照カウントが2になっています。
3. Rcの重要な特性
3.1 参照カウント
Rc
の最も重要な特性は、内部で参照カウントを管理している点です。これは、複数のRc
インスタンスが同じデータを参照する際に、どれか1つの参照が解放されると、他の参照がまだデータを保持している限り、データを解放せずに済むようにするためです。参照カウントは、Rc::strong_count
を使って確認できます。
3.2 不変なデータ
Rc
は、データが不変であることを前提として設計されています。つまり、Rc
の複数のインスタンスが同じデータを参照している場合、そのデータを変更することはできません。データを変更したい場合は、Rc
を使う必要があります。RefCell
は、内部のデータを変更可能にするための仕組みを提供します。
3.3 スレッド間の共有
Rc
は、スレッドセーフではありません。スレッド間でデータを共有する必要がある場合は、Arc
(Atomic Reference Counted)を使うべきです。Arc
は、Rc
と同じく参照カウントを用いますが、スレッドセーフであるため、複数のスレッドで共有することができます。
4. Rcの注意点
4.1 メモリリーク
Rc
は、参照カウントが0になると自動的にメモリを解放しますが、循環参照がある場合、メモリリークが発生する可能性があります。例えば、2つのRc
が互いに参照し合っていると、それぞれの参照カウントが0になることがなく、メモリが解放されません。このような状況を防ぐためには、Weak
型を使うことが推奨されます。
4.2 同期の問題
前述のように、Rc
はスレッドセーフではないため、複数のスレッドからアクセスする場合には注意が必要です。スレッドセーフにするためには、Arc
を使うか、Rc
をロックしてアクセスする方法を考える必要があります。
5. Rcの実世界での使用例
Rc
は、主に以下のようなケースで有用です。
- グラフ構造の管理:複数のノードが互いに参照し合う場合、
Rc
を使うことで、所有権を共有しながらデータを扱うことができます。 - GUIプログラム:GUIアプリケーションでは、複数のウィジェットやコンポーネントが同じデータを参照することが多く、
Rc
はそのようなケースに適しています。
例えば、Rc
を使ってツリー構造を構築する場合、親ノードと子ノードが相互に参照し合う構造が必要になることがあります。その場合にRc
を使用すると、循環参照を避けつつ、データの共有が可能になります。
まとめ
Rc
は、Rustにおけるスマートポインタの一種で、参照カウントを利用して複数の所有者が同じデータを共有できるようにします。これにより、複数の場所でデータを使いたい場合に便利ですが、スレッドセーフではないため、スレッド間でのデータ共有にはArc
を使う必要があります。また、循環参照に注意し、必要に応じてWeak
を使うことでメモリリークを防ぐことができます。