Rc と Arc の違い
参照カウント方式のスマートポインタである std::rc::Rc と std::sync::Arc の違いについて説明します。
Rc(Reference Counted) とは
Rc
はスレッド非安全な参照カウント型のスマートポインタです。データを複数の所有者で共有したい場合に使用します。しかし、単一スレッド内でのみ使用可能という制限があります。メモリ使用量は Arc
よりも少なくなります。
use std::rc::Rc;
fn main() {
let value = Rc::new(5); // 新しい参照カウント付きの値を作成
let value_a = value.clone(); // 参照カウントが2になる
let value_b = value.clone(); // 参照カウントが3になる
println!("value_a: {}", value_a); // 5
println!("value_b: {}", value_b); // 5
// value_a と value_b が有効な間は value はドロップされない
}
この例では、Rc::new(5)
で新しいRc
インスタンスを作成しています。clone()
メソッドで参照をコピーすると、参照カウントが増えていきます。最後の参照が無効になったときにデータが解放されます。
Arc(Atomic Reference Counted) とは
Arc
はスレッド安全な参照カウント型のスマートポインタです。Rc
と同様にデータを複数の所有者で共有しますが、複数のスレッド間でも安全に使用できるのが大きな違いです。その分メモリ使用量が大きくなります。
use std::sync::Arc;
use std::thread;
fn main() {
let value = Arc::new(5);
let value_a = value.clone();
let value_b = value.clone();
let thread_a = thread::spawn(move || {
println!("Thread A: {}", value_a);
});
let thread_b = thread::spawn(move || {
println!("Thread B: {}", value_b);
});
thread_a.join().unwrap();
thread_b.join().unwrap();
}
この例では、Arc::new(5)
で新しいArc
インスタンスを作成しています。value_a
とvalue_b
は同じデータを参照しています。それぞれ別のスレッドで値にアクセスできることがポイントです。Arc
ならスレッド安全に値を共有できます。
メモリ使用量の違い
Rc
はArc
より少ないメモリを消費します。これはArc
がスレッド安全性を実現するために追加のメタデータを持つためです。単一スレッドで使う場合はRc
を使った方がメモリ効率が良くなります。
[備考] 可変アクセスについて
Rc
とArc
はどちらも不変データの共有のみをサポートしており、可変アクセスはできません。
use std::{rc::Rc, sync::Arc};
fn main() {
let a = Rc::new([1,3,2]);
a.sort(); // Rcが保持するデータは不変なので変更できない
let b = Arc::new([1,3,2]);
b.sort(); // Arcが保持するデータは不変なので変更できない
let mut c = Rc::new([1,3,2]);
c.sort(); // Rcが保持するデータは不変なので変更できない
let mut d = Arc::new([1,3,2]);
d.sort(); // Arcが保持するデータは不変なので変更できない
}
Rc
とArc
が保持するデータ自体が不変であるため、mut
を付けてもデータを変更することはできません。
これは、Rustの所有権ルールに由来します。
- 所有権は1つのスレッドにしか存在できない
- 可変な参照も1つしか存在できない
そのため、Rc
やArc
で可変アクセスを許可すると、複数の所有者から同時にデータを書き換えようとした場合にデータ競合が発生する可能性があります。
このように、Rc
とArc
はデータの不変性を保証するためにデータの変更を許可していません。
可変アクセスが必要な場合は、内部可変性を利用する必要があります。
内部可変性を実現する主な方法は以下の3つです。
-
RefCell<T>
:単一スレッド内での可変借用を実現します。スレッド安全ではありません。 -
Mutex<T>
:スレッド安全な可変借用を実現します。一度に1つのスレッドのみがデータにアクセスできます。 -
RwLock<T>
:複数の読み取りと1つの書き込みを同時に許可する、スレッド安全な内部可変性を実現します。
まとめ
-
Rc
はスレッド非安全な参照カウント型のスマートポインタ -
Arc
はスレッド安全な参照カウント型のスマートポインタ -
Rc
はメモリ使用量が少ない -
Arc
はスレッド間でデータを安全に共有できる - 単一スレッドでデータ共有が必要ならば
Rc
を使う - 複数スレッド間でデータ共有が必要ならば
Arc
を使う -
Rc
とArc
はどちらも不変データの共有のみをサポートしており、可変アクセスはできない
Discussion