はじめに
今日もRustのコードを読んでいたら,
struct MediaChannel {
...
metadata: Option<Rc<StreamMetadata>>,
...
}
Rc<T>
みたいなやつが出てきて,役割とか使い方とか色々とわからなかったので調べてみました。
Rc<T>
とは
「プログラミング言語Rust」によると,
Rc は参照カウンタを持つポインタです。 言い換えると、これを使えば、あるデータを「所有する」複数のポインタを持つことができるようになるということです。そして、全てのポインタがスコープから外れたとき、そのデータは削除されます(デストラクタが実行されます)。
と書いてあります。
どういうことなのか説明
「プログラミングRust」も参考にしていますが,
所有権
まず前提として,Rustのほとんどの値は唯一の所有者を持ちます。
Rustではこの所有権システムによってメモリ管理をしています。
Rustで値がメモリに保管されるとき,その値は変数に格納されています。
そして,その変数のことを値の所有者とみなします。
Rustにはスコープの概念があり,変数がそのスコープから外れたとき,その変数は所有権を失って自分の所有している値をメモリから開放します。
しかし,この「値一つに対して所有者が一つ」のシステムだけでは不便なときがあります。
例えば,値A
を使っているすべてのものが使い終わるまで値A
には生存しててほしいタイミングなどです。
こういうときに役立つのがRc<T>
です。
参照カウンタ
RcはReference Counterの略で,参照カウンタとも呼ばれます。
ひとつコードを例にあげます。
use std::rc::Rc;
fn main() {
let s: Rc<String> = Rc::new("Live".to_string());
let t = s.clone();
let u = s.clone();
}
最初の変数s
には,ヒープに保存されているString
に対する参照が格納されています。
その後の行では,変数s
をクローンしてそれぞれ変数t
, u
を作っています。
s
, t
, u
はそれぞれスタックに保管されており,いずれもヒープに保存されているString
を参照しています。
ちなみにそのString
は,また別の場所にある"Live"
という文字列型のデータを参照しています。
さて,ためしにRcポインタに所有される文字列の最後に何らかのテキストを追加してみます。
use std::rc::Rc;
fn main() {
let s: Rc<String> = Rc::new("Live".to_string());
let t = s.clone();
let u = s.clone();
s.push_str(" forever")
}
しかし,このコードに対してRustのコンパイラはエラーを吐いてしまいます。
※Rust Playground調べ
error[E0596]: cannot borrow data in an `Rc` as mutable
--> src/main.rs:8:5
|
8 | s.push_str(" forever")
| ^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc<std::string::String>`
Rustは,Rcポインタから参照されているものは共有されるものと判断するので,不変であるべきだと判断するみたいです。
このstd::rc::Rc
は他の言語にも出てくる参照カウンタと同じような感じみたいです。
まとめ
std::rc::Rc
を使うと所有権システムとは違った変数の生存期間を作ってあげることができます。
それは,複数の変数で所有権を共有することで成り立たせることができます。
また,共有された値はコンパイラによって不変と判断されます。
なかなかドキュメントを読んだだけだと実際に上手に使えない所有権システムたちですが,がんばって理解していこうと思います。
参考リンク
https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/choosing-your-guarantees.html
https://doc.rust-lang.org/std/rc/struct.Rc.html
https://qiita.com/ksato9700/items/312be99d8264b553b193