智能指標
智能指標(序)
相關的概念
- 指標:一個變數在記憶體中包含的是一個地址(指向其它資料)
- Rust 中最常見的指標就是”參考“
- 參考:
- 使用 &
- 借用它指向的值
- 沒有其余開銷
- 最常見的指標型別
智能指標
- 智能指標是這樣一些資料結構:
- 行為和指標相似
- 有額外的元資料和功能
參考計數(Reference counting)智能指標型別
- 通過記錄所有者的數量,使一份資料被多個所有者同時持有
- 并在沒有任何所有者時自動清理資料
參考和智能指標的其它不同
- 參考:只借用資料
- 智能指標:很多時候都擁有它所指向的資料
智能指標的例子
-
String 和
Vec<T>
-
都擁有一片記憶體區域,且允許用戶對其操作
-
還擁有元資料(例如容量等)
-
提供額外的功能或保障(String 保障其資料是合法的 UTF-8 編碼)
智能指標的實作
- 智能指標通常使用 Struct 實作,并且實作了:
- Deref 和 Drop 這兩個 trait
- Deref trait:允許智能指標 struct 的實體像參考一樣使用
- Drop trait:允許你自定義當智能指標實體走出作用域時的代碼
本章內容
- 介紹標準庫中常見的智能指標
Box<T>
:在 heap 記憶體上分配值Rc<T>
:啟用多重所有權的參考計數型別Ref<T>
和RefMut<T>
,通過RefCell<T>
訪問:在運行時而不是編譯時強制借用規則的型別
- 此外:
- 內部可變模型(interior mutability pattern):不可變型別暴露出可修改其內部值的 API
- 參考回圈(reference cycles):它們如何泄露記憶體,以及如何防止其發生,
一、使用Box<T>
來指向 Heap 上的資料
Box<T>
Box<T>
是最簡單的智能指標:- 允許你在 heap 上存盤資料(而不是 stack)
- stack 上是指向 heap 資料的指標
- 沒有性能開銷
- 沒有其它額外功能
- 實作了 Deref trait 和 Drop trait
Box<T>
的常用場景
- 在編譯時,某型別的大小無法確定,但使用該型別時,背景關系卻需要知道它的確切大小,
- 當你有大量資料,想移交所有權,但需要確保在操作時資料不會被復制,
- 使用某個值時,你只關心它是否實作了特定的 trait,而不關心它的具體型別,
使用Box<T>
在heap上存盤資料
fn main() {
let b = Box::new(5);
println!("b = {}", b);
} // b 釋放存在 stack 上的指標 heap上的資料
使用 Box 賦能遞回型別
- 在編譯時,Rust需要知道一個型別所占的空間大小
- 而遞回型別的大小無法再編譯時確定
- 但 Box 型別的大小確定
- 在遞回型別中使用 Box 就可解決上述問題
- 函式式語言中的 Cons List
關于 Cons List
- Cons List 是來自 Lisp 語言的一種資料結構
- Cons List 里每個成員由兩個元素組成
- 當前項的值
- 下一個元素
- Cons List 里最后一個成員只包含一個 Nil 值,沒有下一個元素 (Nil 終止標記)
Cons List 并不是 Rust 的常用集合
- 通常情況下,Vec
是更好的選擇 - (例子)創建一個 Cons List
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
enum List { // 報錯
Cons(i32, List),
Nil,
}
- (例)Rust 如何確定為列舉分配的空間大小
enum Message {
Quit,
Move {x: i32, y: i32},
Write(String),
ChangeColor(i32, i32, i32),
}
使用 Box 來獲得確定大小的遞回型別
- Box
是一個指標,Rust知道它需要多少空間,因為: - 指標的大小不會基于它指向的資料的大小變化而變化
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1,
Box::new(Cons(2,
Box::new(Cons(3,
Box::new(Nil))))));
}
enum List {
Cons(i32, Box<List>),
Nil,
}
- Box
: - 只提供了”間接“存盤和 heap 記憶體分配的功能
- 沒有其它額外功能
- 沒有性能開銷
- 適用于需要”間接“存盤的場景,例如 Cons List
- 實作了 Deref trait 和 Drop trait
二、Deref Trait(1)
Deref Trait
- 實作 Deref Trait 使我們可以自定義解參考運算子 * 的行為,
- 通過實作 Deref,智能指標可像常規參考一樣來處理
解參考運算子
- 常規參考是一種指標
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
把 Box<T>
當作參考
Box<T>
可以替代上例中的參考
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
定義自己的智能指標
Box<T>
被定義成擁有一個元素的 tuple struct- (例子)
MyBox<T>
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);
assert_eq!(5, *y);
}
實作 Deref Trait
- 標準庫中的 Deref trait 要求我們實作一個 deref 方法:
- 該方法借用 self
- 回傳一個指向內部資料的參考
- (例子)
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // *(y.deref())
}
三、Deref Trait (2)
函式和方法的隱式解參考轉化(Deref Coercion)
- 隱式解參考轉化(Deref Coercion)是為函式和方法提供的一種便捷特性
- 假設 T 實作了 Deref trait:
- Deref Coercion 可以把 T 的參考轉化為 T 經過 Deref 操作后生成的參考
- 當把某型別的參考傳遞給函式或方法時,但它的型別與定義的引數型別不匹配:
- Deref Coercion 就會自動發生
- 編譯器會對 deref 進行一系列呼叫,來把它轉為所需的引數型別
- 在編譯時完成,沒有額外性能開銷
use std::ops::Deref;
fn hello(name: &str) {
println!("Hello, {}", name);
}
fn main() {
let m = MyBox::new(String::from("Rust"));
// &m &MyBox<String> 實作了 deref trait
// deref &String
// deref &str
hello(&m);
hello(&(*m)[..]);
hello("Rust");
}
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // *(y.deref())
}
解參考與可變性
- 可使用 DerefMut trait 多載可變參考的 * 運算子
- 在型別和 trait 在下列三種情況發生時,Rust會執行 deref coercion:
- 當 T:Deref<Target=U>,允許 &T 轉換為 &U
- 當 T:DerefMut<Target=U>,允許 &mut T 轉換為 &mut U
- 當 T:Deref<Target=U>,允許 &mut T 轉換為 &U
四、Drop Trait
Drop Trait
- 實作 Drop Trait,可以讓我們自定義當值將要離開作用域時發生的動作,
- 例如:檔案、網路資源釋放等
- 任何型別都可以實作 Drop trait
- Drop trait 只要求你實作 drop 方法
- 引數:對self 的可變參考
- Drop trait 在預匯入模塊里(prelude)
/*
* @Author: QiaoPengjun5162 [email protected]
* @Date: 2023-04-13 21:39:51
* @LastEditors: QiaoPengjun5162 [email protected]
* @LastEditTime: 2023-04-13 21:46:50
* @FilePath: /smart/src/main.rs
* @Description: 這是默認設定,請設定`customMade`, 打開koroFileHeader查看配置 進行設定: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data: `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {data: String::from("my stuff")};
let d = CustomSmartPointer {data: String::from("other stuff")};
println!("CustomSmartPointers created.")
}
運行
smart on master [?] is ?? 0.1.0 via ?? 1.67.1
? cargo run
Compiling smart v0.1.0 (/Users/qiaopengjun/rust/smart)
warning: unused variable: `c`
--> src/main.rs:20:9
|
20 | let c = CustomSmartPointer {data: String::from("my stuff")};
| ^ help: if this is intentional, prefix it with an underscore: `_c`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `d`
--> src/main.rs:21:9
|
21 | let d = CustomSmartPointer {data: String::from("other stuff")};
| ^ help: if this is intentional, prefix it with an underscore: `_d`
warning: `smart` (bin "smart") generated 2 warnings (run `cargo fix --bin "smart"` to apply 2 suggestions)
Finished dev [unoptimized + debuginfo] target(s) in 0.53s
Running `target/debug/smart`
CustomSmartPointers created.
Dropping CustomSmartPointer with data: `other stuff`!
Dropping CustomSmartPointer with data: `my stuff`!
smart on master [?] is ?? 0.1.0 via ?? 1.67.1 took 3.6s
使用 std::mem::drop
來提前 drop 值
- 很難直接禁用自動的 drop 功能,也沒必要
- Drop trait 的目的就是進行自動的釋放處理邏輯
- Rust 不允許手動呼叫 Drop trait 的 drop 方法
- 但可以呼叫標準庫的
std::mem::drop
函式,來提前 drop 值
- 但可以呼叫標準庫的
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data: `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {data: String::from("my stuff")};
drop(c);
let d = CustomSmartPointer {data: String::from("other stuff")};
println!("CustomSmartPointers created.")
}
五、Rc<T>
:參考計數智能指標
Rc<T>
參考計數智能指標
- 有時,一個值會有多個所有者
- 為了支持多重所有權:
Rc<T>
- reference couting(參考計數)
- 追蹤所有到值的參考
- 0 個參考:該值可以被清理掉
Rc<T>
使用場景
- 需要在 heap上分配資料,這些資料被程式的多個部分讀取(只讀),但在編譯時無法確定哪個部分最后使用完這些資料
Rc<T>
只能用于單執行緒場景
例子
Rc<T>
不在預匯入模塊(prelude)Rc::clone(&a)
函式:增加參考計數Rc::strong_count(&a)
:獲得參考計數- 還有
Rc::weak_count
函式
- 還有
- (例子)
- 兩個 List 共享 另一個 List 的所有權
/*
* @Author: QiaoPengjun5162 [email protected]
* @Date: 2023-04-13 22:32:41
* @LastEditors: QiaoPengjun5162 [email protected]
* @LastEditTime: 2023-04-13 22:37:17
* @FilePath: /smart/src/lib.rs
* @Description: 這是默認設定,請設定`customMade`, 打開koroFileHeader查看配置 進行設定: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
enum List {
Cons(i32, Box<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
let b = Cons(3, Box::new(a));
let c = Cons(4, Box::new(a)); // 報錯
}
優化修改一
/*
* @Author: QiaoPengjun5162 [email protected]
* @Date: 2023-04-13 22:32:41
* @LastEditors: QiaoPengjun5162 [email protected]
* @LastEditTime: 2023-04-13 22:45:15
* @FilePath: /smart/src/lib.rs
* @Description: 這是默認設定,請設定`customMade`, 打開koroFileHeader查看配置 進行設定: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
// a.clone() // 深度拷貝操作
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a)); //
}
優化修改二
/*
* @Author: QiaoPengjun5162 [email protected]
* @Date: 2023-04-13 22:32:41
* @LastEditors: QiaoPengjun5162 [email protected]
* @LastEditTime: 2023-04-13 22:51:04
* @FilePath: /smart/src/lib.rs
* @Description: 這是默認設定,請設定`customMade`, 打開koroFileHeader查看配置 進行設定: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes of scope = {}", Rc::strong_count(&a));
}
Rc::clone()
vs 型別的 clone() 方法
Rc::clone()
:增加參考,不會執行資料的深度拷貝操作- 型別的 clone():很多會執行資料的深度拷貝操作
Rc<T>
-
Rc<T>
通過不可變參考,使你可以在程式不同部分之間共享只讀資料 -
但是,如何允許資料變化呢?
六、RefCell<T>
和內部可變性
內部可變性(interior mutability)
- 內部可變性是Rust的設計模式之一
- 它允許你在只持有不可變參考的前提下對資料進行修改
- 資料結構中使用了 unsafe 代碼來繞過 Rust 正常的可變性和借用規則
RefCell<T>
- 與
Rc<T>
不同,RefCell<T>
型別代表了其持有資料的唯一所有權,
回憶一下:借用規則
- 在任何給定的時間里,你要么只能擁有一個可變參考,要么只能擁有任意數量的不可變參考
- 參考總是有效的
RefCell<T>
與 Box<T>
的區別
Box<T>
- 編譯階段強制代碼遵守借用規則
- 否則出現錯誤
RefCell<T>
- 只會在運行時檢查借用規則
- 否則觸發 panic
借用規則在不同階段進行檢查的比較
編譯階段
- 盡早暴露問題
- 沒有任何運行時開銷
- 對大多數場景是最佳選擇
- 是Rust的默認行為
運行時
- 問題暴露延后,甚至到生產環境
- 因借用計數產生些許性能損失
- 實作某些特定的記憶體安全場景(不可變環境中修改自身資料)
RefCell<T>
- 與
Rc<T>
相似,只能用于單執行緒場景
選擇Box<T>
、Rc<T>
、RefCell<T>
的依據
說明 | Box<T> |
Rc<T> |
RefCell<T> |
---|---|---|---|
同一資料的所有者 | 一個 | 多個 | 一個 |
可變性、借用檢查 | 可變、不可變借用(編譯時檢查) | 不可變借用(編譯時檢查) | 可變、不可變借用(運行時檢查) |
- 其中:即便
RefCell<T>
本身不可變,但仍能修改其中存盤的值
內部可變性:可變的借用一個不可變的值
fn main() {
let x = 5;
let y = &mut x; // 報錯 cannot borrow as mutable
}
例子:
pub trait Message {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: 'a + Message> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &T, value: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = https://www.cnblogs.com/QiaoPengjun/archive/2023/04/16/value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
.send("Urgent warning: You're used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
.send("Warning: You're used up over 75% of your quota!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockMessenger {
sent_messages: Vec<String>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: vec![],
}
}
}
impl Messenger for MockMessenger {
fn send(&mut self, message: &str) { // 報錯
self.sent_messages.push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.len(), 1);
}
}
修改之后:
pub trait Message {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: 'a + Message> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &T, value: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = https://www.cnblogs.com/QiaoPengjun/archive/2023/04/16/value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
.send("Urgent warning: You're used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
.send("Warning: You're used up over 75% of your quota!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
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, message: &str) { // 報錯
self.sent_messages.borrow_mut().push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}
使用RefCell<T>
在運行時記錄借用資訊
- 兩個方法(安全介面):
- borrow 方法
- 回傳智能指標
Ref<T>
,它實作了 Deref
- 回傳智能指標
- borrow_mut 方法
- 回傳智能指標
RefMut<T>
,它實作了 Deref
- 回傳智能指標
- borrow 方法
RefCell<T>
會記錄當前存在多少個活躍的Ref<T>
和RefMut<T>
智能指標:- 每次呼叫 borrow:不可變借用計數加1
- 任何一個
Ref<T>
的值離開作用域被釋放時:不可變借用計數減1 - 每次呼叫 borrow_mut:可變借用計數加1
- 任何一下
RefMut<T>
的值離開作用域被釋放時:可變借用計數減1
- 以此技術來維護借用檢查規則:
- 任何一個給定時間里,只允許擁有多個不可變借用或一個可變借用,
將 Rc<T>
和 RefCell<T>
結合使用來實作一個擁有多重所有權的可變資料
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let value = https://www.cnblogs.com/QiaoPengjun/archive/2023/04/16/Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));
*value.borrow_mut() += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}
運行
refdemo on master [?] is ?? 0.1.0 via ?? 1.67.1
? cargo run
Compiling refdemo v0.1.0 (/Users/qiaopengjun/rust/refdemo)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/refdemo`
a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil))
refdemo on master [?] is ?? 0.1.0 via ?? 1.67.1
?
其它可實作內部可變性的型別
Cell<T>
:通過復制來訪問資料Mutex<T>
:用于實作跨執行緒情形下的內部可變性模式
七、回圈參考可導致記憶體泄漏
Rust可能發生記憶體泄漏
- Rust的記憶體安全機制可以保證很難發生記憶體泄漏,但不是不可能,
- 例如使用
Rc<T>
和RefCell<T>
就可能創造出回圈參考,從而發生記憶體泄漏:- 每個項的參考數量不會變成0,值也不會被處理掉,
use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
enum List {
Cons(i32, RefCell<Rc<List>>),
Nil,
}
impl List {
fn tail(&self) -> Option<&RefCell<Rc<List>>> {
match self {
Cons(_, item) => Some(item),
Nil => None,
}
}
}
fn main() {
let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
println!("a initial rc count = {}", Rc::strong_count(&a));
println!("a next item = {:?}", a.tail());
let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
println!("a rc count after b creation = {}", Rc::strong_count(&a));
println!("b initial rc count = {}", Rc::strong_count(&b));
println!("b next item = {:?}", b.tail());
if let Some(link) = a.tail() {
*link.borrow_mut() = Rc::clone(&b);
}
println!("b rc count after changing a = {}", Rc::strong_count(&b));
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.
// println!("a next item = {:?}", a.tail());
}
防止記憶體泄漏的解決辦法
- 依靠開發者來保證,不能依靠Rust
- 重新組織資料結構:一些參考來表達所有權,一些參考不表達所有權
- 回圈參考中的一部分具有所有權關系,另一部分不涉及所有權關系
- 而只有所有權關系才影響值的清理
防止回圈參考 把Rc<T>
換成Weak<T>
Rc::clone
為Rc<T>
實體的 strong_count 加1,Rc<T>
的實體只有在 strong_count 為0的時候才會被清理Rc<T>
實體通過呼叫Rc::downgrade
方法可以創建值的 Weak Reference (弱參考)- 回傳型別是
Weak<T>
(智能指標) - 呼叫
Rc::downgrade
會為 weak_count 加 1
- 回傳型別是
Rc<T>
使用 weak_count 來追蹤存在多少Weak<T>
- weak_count 不為0并不影響
Rc<T>
實體的清理
Strong vs Weak
- Strong Reference(強參考)是關于如何分享
Rc<T>
實體的所有權 - Weak Reference(弱參考)并不表達上述意思
- 使用 Weak Reference 并不會創建回圈參考:
- 當 Strong Reference 數量為0的時候,Weak Reference 會自動斷開
- 在使用
Weak<T>
前,需保證它指向的值仍然存在:- 在
Weak<T>
實體上呼叫 upgrade 方法,回傳Option<Rc<T>>
- 在
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
children: RefCell::new(vec![]),
});
let branch = Rc::new(Node {
value: 5,
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
}
修改后:
use std::rc::{ Rc, Weak };
use std::cell::RefCell;
#[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![]),
});
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());
}
修改后:
use std::rc::{ Rc, Weak };
use std::cell::RefCell;
#[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![]),
});
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
{
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 strong = {}, weak = {}",
Rc::strong_count(&branch),
Rc::weak_count(&branch),
);
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
本文來自博客園,作者:QIAOPENGJUN,轉載請注明原文鏈接:https://www.cnblogs.com/QiaoPengjun/p/17324022.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550240.html
標籤:其他
上一篇:java -- File類和遞回
下一篇:三天吃透計算機網路八股文