#TheBook #Rust https://doc.rust-jp.rs/book-ja/ch15-00-smart-pointers.html - [[ポインタ]] - [[参照]] - [[借用]]するだけのポインタ - [[スマートポインタ]]は[[ポインタ]]を含む強いやつ - データに複数の[[所有権]]を持たせたりできる - [[借用]]ではなく所有することが多い - [[String]]や[[ベクター]]など ## ヒープのデータを指す`Box<T>`を使用する - [[Boxを使うケース]] - [[コンスリスト]] - `Box<T>`がスコープを抜けると、`Box<T>`が参照している[[ヒープ領域]]のデータも掃除される [[Dropトレイト]]の力 ## Derefトレイトでスマートポインタを普通の参照のように扱う - [[Derefトレイト]]で[[参照外し演算子]]の挙動をカスタマイズ ```rust struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } } fn main() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); // type MyBox<{integer}> cannot be dereferenced [E0614] assert_eq!(5, *y); } ``` このコードは[[Box]]を想定したものだが、[[参照外し演算子]]が使えずエラーになることを確認できる。 ```rust impl<T> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } ``` 上記の[[Derefトレイト]]実装で解決。 [[参照外し型強制]]をちゃんと理解する。暗黙的型変換ともいえる。 - [[参照外し型強制の詳細説明]] - [[参照外し型強制が作用する3つのケース]] ## [[Dropトレイト]]で片づけ時にコードを走らせる ```rust struct CustomSmartPointer { data: String, } impl Drop for CustomSmartPointer { fn drop(&mut self) { eprintln!("self.data = {:?}", self.data); } } fn main() { let c = CustomSmartPointer { data: String::from("my stuff"), }; { println!("In the scope"); let d = CustomSmartPointer { data: String::from("other stuff"), }; println!("End of the scope"); // ここでdropが呼ばれる } println!("CustomSmartPointer created."); // ここでdropが呼ばれる } ``` - `drop`は手動で呼び出せない (エラーになる) - 手動で呼び出したい場合は `std::mem::drop` 関数を使う ## `Rc<T>`は、参照カウンタ方式の[[スマートポインタ]] - [[Rc]] - シングルスレッドの筋書きで使用する - `Rc::clone`で参照数が増えて[[所有権]]が共有される? - これは[[ディープコピー]]ではない - `Tc::strong_count`で参照カウントを取得 - [[Rc]]は[[不変参照]]に対して使うもの ```rust use std::rc::Rc; use List::{Cons, Nil}; enum List { Cons(i32, Rc<List>), Nil, } fn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); eprintln!("1. Rc::strong_count(&a) = {:?}", Rc::strong_count(&a)); let b = Cons(3, Rc::clone(&a)); eprintln!("2. Rc::strong_count(&a) = {:?}", Rc::strong_count(&a)); { let c = Cons(4, Rc::clone(&a)); eprintln!("3. Rc::strong_count(&a) = {:?}", Rc::strong_count(&a)); } eprintln!("4. Rc::strong_count(&a) = {:?}", Rc::strong_count(&a)); } ``` ## `RefCell<T>`と[[内部可変性]]パターン - [[RefCell]] - 開発者は[[借用規則]]に従っていると確信しているが、コンパイラがそれを理解できないときの救済策 - シングルスレッドの筋書きで使用する - [[RcとBoxとRefcellの違い]] - [[内部可変性]] **[[テストダブル]]で[[モック]]を使う場合の例が分かりやすい。** ```rust impl Messenger for MockMessenger { fn send(&self, msg: &str) { // &self が不変参照なのがまずいらしい... // それだとフィールドが不変だからpushができない // とはいえ可変にするとシグニチャがあわないしテストのために可変にするのは違う self.sent_messages.push(String::from(msg)); } } ``` [[RefCell]]で囲って `borrow_mut` で[[可変参照]]を、`borrow`で[[不変参照]]をそれぞれ取得。 ```rust #[cfg(test)] mod tests { use std::cell::RefCell; use super::*; struct MockMessenger { sent_messages: RefCell<Vec<String>>, } impl MockMessenger { fn new() -> MockMessenger { MockMessenger { sent_messages: RefCell::new(vec![]), } } } impl Messenger for MockMessenger { fn send(&self, msg: &str) { self.sent_messages.borrow_mut().push(String::from(msg)); } } #[test] fn it_sends_an_over_75_percent_warning_message() { let mock_messanger = MockMessenger::new(); let mut limit_tracker = LimitTracker::new(&mock_messanger, 100); limit_tracker.set_value(80); assert_eq!(mock_messanger.sent_messages.borrow().len(), 1); } } ``` - `borrow`は`Ref<T>`、`borrow_mut`は`RefMut<T>`を返す - どちらも[[Derefトレイト]]を実装しているので、[[参照]]と同じように扱うことができる 実行時に[[借用規則]]を破ってみる。2つの[[可変参照]]を持たせるコード。 ```rust impl Messenger for MockMessenger { fn send(&self, msg: &str) { let mut one_borrow = self.sent_messages.borrow_mut(); let mut two_borrow = self.sent_messages.borrow_mut(); one_borrow.push(String::from(msg)); two_borrow.push(String::from(msg)); } } ``` 実行すると、`let mut two_borrow...` の行でエラーになる。 ``` already borrowed: BorrowMutError thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at 'already borrowed: BorrowMutError', src\main.rs:60:53 ``` ### 最後のコードがコンパイルエラーになる 以下のように変更しないと使えない...。 ```diff - *value.borrow_mut() += 10; + *(*value).borrow_mut() += 10; ``` 原因は`borrow_mut`違い。`std::borrow:BorrowMut`を使った場合、つまり下記の場合はエラーになってしまう。 ```rust use std::borrow::BorrowMut; ``` ```rust #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_borrow", issue = "91522")] impl<T: ?Sized> const BorrowMut<T> for T { fn borrow_mut(&mut self) -> &mut T { self } } ``` [[RefCell]]の`borrow_mut`が呼ばれるようにしないといけない。 ```rust #[stable(feature = "rust1", since = "1.0.0")] #[inline] #[track_caller] pub fn borrow_mut(&self) -> RefMut<'_, T> { self.try_borrow_mut().expect("already borrowed") } ``` ## 循環参照は、メモリをリークすることもある - [[Weak]] - `Rc::downgrade`を呼び出すことで`weak_count`が1増える - `weak_count`は0にならなくても`Rc<T>`を破棄できる - そのため`Weak<T>`の[[参照]]は信用できない - `Weak<T>`の`upgrade`[[メソッド (Rust)|メソッド]]で存在を確認する ```rust 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![]), }); 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); eprintln!("leaf.parent = {:?}", leaf.parent.borrow().upgrade()); // 今回は循環参照しない eprintln!("leaf = {:?}", leaf); } ``` 強カウントと弱カウントの理解はあっていた。すごく時間はかかったし混乱するけど。。 ```rust fn main() { let leaf = Rc::new(Node { value: 3, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]), }); // leaf: strong = 1, weak = 0 print_counts(&leaf); { let branch = Rc::new(Node { value: 5, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]), }); // leaf: strong = 2, weak = 0 print_counts(&leaf); // branch: strong = 1, weak = 0 print_counts(&branch); *leaf.parent.borrow_mut() = Rc::downgrade(&branch); // leaf: strong = 2, weak = 0 print_counts(&leaf); // branch: strong = 1, weak = 1 print_counts(&branch); } println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); // leaf: strong = 1, weak = 0 print_counts(&leaf); } ```