はじめに
#NO IMAGEThe Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
を読んでいる
- やば、気を抜くと光らせるの忘れるな
お勉強
#- artoのprやりすぎてしまって反省している
- こんなに難しいんだなとわかった
- 今日は優しめ15分
NO IMAGE循環参照は、メモリをリークすることもある - The Rust Programming Language 日本語版
- ここやる
メモ
#- ちゃんと読んだらシンプルに循環してた
- 一応回避する方法ありそうな予感
fn main() {
let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
// "aの最初の参照カウント = {}"
println!("a initial rc count = {}", Rc::strong_count(&a));
// "aの次の要素は = {:?}"
println!("a next item = {:?}", a.tail());
let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
// "b作成後のaの参照カウント = {}"
println!("a rc count after b creation = {}", Rc::strong_count(&a));
// "bの最初の参照カウント = {}"
println!("b initial rc count = {}", Rc::strong_count(&b));
// "bの次の要素 = {:?}"
println!("b next item = {:?}", b.tail());
if let Some(link) = a.tail() {
*link.borrow_mut() = Rc::clone(&b);
}
// "aを変更後のbの参照カウント = {}"
println!("b rc count after changing a = {}", Rc::strong_count(&b));
// "aを変更後のaの参照カウント = {}"
println!("a rc count after changing a = {}", Rc::strong_count(&a));
// Uncomment the next line to see that we have a cycle;
// it will overflow the stack
// 次の行のコメントを外して循環していると確認してください;
// スタックオーバーフローします
// // "aの次の要素 = {:?}"
// println!("a next item = {:?}", a.tail());
}
$ cargo run
Compiling cons-list v0.1.0 (file:///projects/cons-list)
Finished dev [unoptimized + debuginfo] target(s) in 0.53s
Running `target/debug/cons-list`
a initial rc count = 1
a next item = Some(RefCell { value: Nil })
a rc count after b creation = 2
b initial rc count = 1
b next item = Some(RefCell { value: Cons(5, RefCell { value: Nil }) })
b rc count after changing a = 2
a rc count after changing a = 2
- なげーなと思ったがシンプルに参照countとか余計なものが多いだけで最後のコメントアウトだけだった
この例で循環参照を作成してしまうという結果は、それほど悲壮なものではありません:
循環参照を作った直後にプログラムが終了するからです。しかしながら、 より複雑なプログラムが多くのメモリを循環で確保し長い間その状態を保ったら、プログラムは必要以上のメモリを使用し、 使用可能なメモリを枯渇させてシステムを参らせてしまう可能性があります。
-
まーこれが怖いよな
-
Weak<T>があるらしい- そういえばWeakって弱いか
- Weak PointのWeak
Weak
のupgradeメソッドを呼び出すことでこれをしてください。
このメソッドはOption<Rc>を返します。
-
おおーなるほど、そらそうだわ
- あるかどうかわからないから
Optionつかうよね - Rustのコンテキストがわかってきた
- あるかどうかわからないから
-
ということはこいつも
Derefを実装してないな
NO IMAGE循環参照は、メモリをリークすることもある - The Rust Programming Language 日本語版
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
// "leafの親 = {:?}"
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}
- 無理やり使っているように見えてならん
NO IMAGE循環参照は、メモリをリークすることもある - The Rust Programming Language 日本語版
カウントや値のドロップを管理するロジックは全て、
Rc<T>やWeak<T>とそのDropトレイトの実装に組み込まれています。
Nodeの定義で子供から親への関係はWeak<T>参照になるべきと指定することで、 循環参照やメモリリークを引き起こさずに親ノードに子ノードを参照させたり、その逆を行うことができます。
- なるほどなー
- 関係ないけどこれ思いだした
leaf作成後、そのRc
の強カウントは1、弱カウントは0になります。
内側のスコープでbranchを作成し、 leafに紐付け、この時点でカウントを出力すると、branchのRcの強カウントは1、 弱カウントも1になります(leaf.parentがWeak でbranchを指しているため)。
leafのカウントを出力すると、強カウントが2になっていることがわかります。
branchが今は、 branch.childrenに格納されたleafのRcのクローンを持っているからですが、 それでも弱カウントは0でしょう。
- いや、いいたいことは分かるが文章だとつらいな...
まとめ
#Weak<T>があるということがわかった- 循環参照を回避できる
- でもそもそも循環参照になってしまっているのなら作りが悪いよなって思ってしまうので頭に入ってこなかった
次はよいよ並行処理!!!!!!!!!