Rust 智能指針的實(shí)現(xiàn)
涵蓋:Box、Rc/Arc、RefCell/Mutex、Deref/Drop trait、循環(huán)引用等
重點(diǎn):智能指針選型、線程安全約束、內(nèi)部可變性、內(nèi)存泄漏預(yù)防
一、基礎(chǔ)概念
1. 普通裸指針*const T / *mut T
無所有權(quán)、無自動(dòng)內(nèi)存管理、無借用檢查,僅存內(nèi)存地址,操作必須 unsafe。
2. 智能指針本質(zhì)
實(shí)現(xiàn)了 Deref + Drop trait 的結(jié)構(gòu)體
- Deref:自動(dòng)解引用,像普通引用一樣使用;
- Drop:離開作用域自動(dòng)釋放堆內(nèi)存,防止內(nèi)存泄漏;
- 變量本體存在棧,棧內(nèi)只存指針/元數(shù)據(jù),真實(shí)數(shù)據(jù)存堆;
- 自帶所有權(quán)、借用、計(jì)數(shù)、鎖等安全邏輯,不用手動(dòng)管理內(nèi)存。
區(qū)分普通引用 & 智能指針
| 類型 | 說明 |
|---|---|
| &T | 借用,不擁有數(shù)據(jù),生命周期依附所有者 |
| 智能指針 | 擁有堆數(shù)據(jù)所有權(quán),自行管理內(nèi)存生命周期 |
二、核心智能指針分類
獨(dú)占所有權(quán):Box
底層結(jié)構(gòu)
struct Box<T>(*mut T);
棧上僅一根堆指針,T 完整分配在堆。
核心特性
- 唯一所有者,同一時(shí)間只能有一個(gè) Box;
- 無額外開銷,性能等同于裸指針;
- 自動(dòng) Drop,出作用域釋放堆;
- 實(shí)現(xiàn)
Deref<Target=T>,可直接.訪問字段。
適用場景
- 遞歸結(jié)構(gòu)體/枚舉(解決無限尺寸)
struct Node {
val: i32,
next: Option<Box<Node>>
}
- 超大棧值,避免棧溢出,轉(zhuǎn)移到堆;
- 構(gòu)造 trait 對象
Box<dyn Trait>(胖指針:數(shù)據(jù)指針 + vtable); - FFI、需要堆分配的場景。
示例
let b = Box::new(10);
println!("{}", *b); // Deref 解引用
println!("{}", b); // 自動(dòng)隱式解引用
單線程共享所有權(quán):Rc / Weak
Rc 底層
堆上分配 RcInner<T>,包含:
- 強(qiáng)引用計(jì)數(shù) strong:持有數(shù)據(jù)的 Rc 數(shù)量;
- 弱引用計(jì)數(shù) weak:Weak 數(shù)量;
- 真實(shí)數(shù)據(jù) T。
規(guī)則
Rc<T>多份克隆共享同一堆數(shù)據(jù),單線程專用,不支持多線程;- 每次
.clone()僅??截愔羔槪焉?strong+1; - 所有 Rc 銷毀 strong=0,釋放數(shù)據(jù);weak 不阻止釋放;
- 內(nèi)部不可變:
Rc只讀,如需修改搭配RefCell<T>。
Weak 弱指針
- 不增加強(qiáng)計(jì)數(shù),不阻止數(shù)據(jù)釋放;
- 調(diào)用
upgrade()嘗試轉(zhuǎn)為Option<Rc<T>>,數(shù)據(jù)已銷毀返回 None; - 解決循環(huán)引用內(nèi)存泄漏。
示例
use std::rc::Rc;
let a = Rc::new("hello".to_string());
let b = Rc::clone(&a); // 僅計(jì)數(shù) +1,不拷貝字符串
println!("strong count: {}", Rc::strong_count(&a));
多線程共享所有權(quán):Arc / Weak
和 Rc 邏輯完全一致,區(qū)別:
| 特性 | Rc | Arc |
|---|---|---|
| 計(jì)數(shù)方式 | 普通整數(shù) | 原子操作 AtomicUsize |
| 線程安全 | ? 單線程 | ? 跨線程 |
| 性能開銷 | 低 | 略高(原子指令) |
搭配內(nèi)部可變性:Arc<Mutex<T>> / Arc<RwLock<T>>
內(nèi)部可變性容器(棧存指針,堆存標(biāo)記 + 數(shù)據(jù))
1. RefCell 單線程內(nèi)部可變性
突破外部不可變限制,運(yùn)行時(shí)借用檢查(而非編譯期)
- 存儲(chǔ):堆存數(shù)據(jù) + 借用狀態(tài)標(biāo)記(借出數(shù)量、是否可變)
- 方法:
.borrow()→Ref<T>不可變借用(可多個(gè)同時(shí)存在).borrow_mut()→RefMut<T>可變借用(同一時(shí)間只能一個(gè))
- 違反規(guī)則運(yùn)行時(shí) panic,非編譯報(bào)錯(cuò)。
搭配 Rc 實(shí)現(xiàn)單線程共享可變數(shù)據(jù):Rc<RefCell<T>>
use std::rc::Rc; use std::cell::RefCell; let val = Rc::new(RefCell::new(0)); *val.borrow_mut() += 1;
2. Cell
內(nèi)部可變性,只適合 Copy 小類型
- 小 Copy 類型直接棧存儲(chǔ),無堆分配;
- 無借用,直接
.get()/.set(),不會(huì)觸發(fā) panic; - 不能包裹 String、Vec 非 Copy 類型。
3. Mutex 互斥鎖(多線程)
多線程內(nèi)部可變性,同一時(shí)間僅一個(gè)線程訪問數(shù)據(jù):
.lock()獲取鎖,返回MutexGuard<T>,阻塞等待;- 中毒:持有鎖線程 panic,鎖永久失效;
- 搭配 Arc:
Arc<Mutex<T>>多線程共享可變資源。
4. RwLock 讀寫鎖
- 多個(gè)讀共存,寫?yīng)氄?;讀多寫少場景性能優(yōu)于 Mutex。
三、Deref 自動(dòng)解引用(智能指針核心 trait)
Deref trait 定義
trait Deref {
type Target;
fn deref(&self) -> &Self::Target;
}
實(shí)現(xiàn)后支持:
*智能指針手動(dòng)解引用;- 自動(dòng)隱式解引用,直接調(diào)用內(nèi)部字段/方法;
- 解引用強(qiáng)制轉(zhuǎn)換:函數(shù)傳參自動(dòng)逐層解引用。
示例:Box 自動(dòng)解引用
struct Point { x: i32, y: i32 }
let p = Box::new(Point { x: 1, y: 2 });
println!("{}", p.x); // 自動(dòng) deref,無需 *p
DerefMut 可變解引用
trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
Box<T>、RefMut、MutexGuard 均實(shí)現(xiàn),支持 *mut_ptr = xxx 修改。
四、Drop 析構(gòu) trait
所有所有權(quán)智能指針都實(shí)現(xiàn) Drop,離開作用域自動(dòng)執(zhí)行:
| 智能指針 | Drop 行為 |
|---|---|
| Box | 釋放堆上 T |
| Rc/Arc | strong 計(jì)數(shù) -1,計(jì)數(shù)歸零釋放堆 |
| RefCell/Mutex | 釋放內(nèi)部堆數(shù)據(jù) |
手動(dòng)提前釋放:std::mem::drop(x),主動(dòng)轉(zhuǎn)移所有權(quán)觸發(fā) Drop。
五、智能指針選型對照表
| 指針 | 線程安全 | 所有權(quán)模式 | 可變性方案 | 典型用途 |
|---|---|---|---|---|
| Box | 單/多線程均可 | 獨(dú)占唯一 | 外部 mut | 遞歸類型、堆分配、trait 對象 |
| Rc | ? 單線程 | 共享多所有者 | Rc<RefCell> | 單線程多層共享 |
| Arc | ? 多線程 | 共享多所有者 | Arc<Mutex/RwLock> | 跨線程共享資源 |
| RefCell | ? 單線程 | 獨(dú)占 | 運(yùn)行時(shí)借用檢查 | 單線程內(nèi)部可變性 |
| Mutex | ? 多線程 | 獨(dú)占 | 互斥排他訪問 | 多線程可變共享 |
| RwLock | ? 多線程 | 獨(dú)占 | 讀共享寫排他 | 讀多寫少并發(fā) |
| Weak | 跟隨 Rc/Arc | 弱引用不計(jì)數(shù) | 無 | 解決循環(huán)引用 |
六、關(guān)鍵使用規(guī)則
1. 所有權(quán)與拷貝
| 指針 | 行為 |
|---|---|
| Box | move 語義,賦值轉(zhuǎn)移所有權(quán);clone 深拷貝堆數(shù)據(jù) |
| Rc/Arc | .clone() 僅增加計(jì)數(shù),零堆拷貝 |
| Weak | 不能直接使用,必須 upgrade 轉(zhuǎn) Rc/Arc |
2. 循環(huán)引用泄漏問題
單純 Rc/Arc 雙向引用會(huì)造成計(jì)數(shù)永遠(yuǎn)不為 0,內(nèi)存泄漏;
解決:其中一端改用 Weak 弱指針。
3. 線程安全約束 Send / Sync
| 指針 | Send/Sync |
|---|---|
| Box | T 滿足 Send/Sync 則 Box 滿足 |
| Rc | 不實(shí)現(xiàn) Send/Sync,禁止跨線程 |
| Arc | 實(shí)現(xiàn) Send+Sync,可跨線程 |
| Mutex | Send+Sync |
4. 解引用強(qiáng)制多態(tài)(函數(shù)傳參自動(dòng)轉(zhuǎn)換)
fn read(s: &str) {}
let b = Box::new("test".to_string());
read(&b);
// &Box<String> → &String → &str 自動(dòng)多層 deref
七、高頻易錯(cuò)點(diǎn)
?? 以下易錯(cuò)點(diǎn)需特別注意
- Rc 傳給 spawn 線程直接編譯報(bào)錯(cuò),必須換 Arc;
- RefCell 在同一作用域同時(shí)存在可變 + 不可變借用,運(yùn)行時(shí) panic;
- 忘記循環(huán)引用搭配 Weak,長期運(yùn)行內(nèi)存持續(xù)上漲;
- Mutex lock() 在線程 panic 后鎖中毒,后續(xù)全部阻塞;
- Box::new 超大數(shù)組會(huì)先在棧構(gòu)造再拷貝堆,超大數(shù)組優(yōu)先 Vec;
- 混淆借用 &T 和智能指針:&無所有權(quán),智能指針擁有堆內(nèi)存;
- Cell 僅支持 Copy 類型,包裹 String 編譯報(bào)錯(cuò)。
八、速記口訣
智能指針實(shí)現(xiàn) Deref+Drop,棧存指針堆存數(shù)據(jù);
Box 獨(dú)占單所有者,遞歸 trait 對象必備;
Rc 單線程共享,Arc 多線程原子計(jì)數(shù);
Weak 弱引用防循環(huán)泄漏,upgrade 轉(zhuǎn)為強(qiáng)指針;
RefCell 單線程內(nèi)部可變,運(yùn)行借用檢查;
Mutex/RwLock 多線程鎖,讀寫分離性能更佳;
Deref 自動(dòng)解引用,點(diǎn)號直接訪問內(nèi)部字段;
Rc 禁止跨線程,跨線程共享只用 Arc。
到此這篇關(guān)于Rust 智能指針的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Rust 智能指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vscode搭建rust開發(fā)環(huán)境的圖文教程
Rust 是一種系統(tǒng)編程語言,它專注于內(nèi)存安全、并發(fā)和性能,本文主要介紹了vscode搭建rust開發(fā)環(huán)境的圖文教程,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
Rust重載運(yùn)算符之復(fù)數(shù)四則運(yùn)算的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Rust如何實(shí)現(xiàn)復(fù)數(shù)以及復(fù)數(shù)的四則運(yùn)算,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08
如何使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼
這篇文章主要介紹了使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
libbpf和Rust開發(fā)ebpf程序?qū)崙?zhàn)示例
這篇文章主要為大家介紹了libbpf和Rust開發(fā)ebpf程序?qū)崙?zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
rust 一個(gè)日志緩存記錄的通用實(shí)現(xiàn)方法
本文給出了一個(gè)通用的設(shè)計(jì)模式,通過建造者模式實(shí)例化記錄對象,可自定義格式化器將實(shí)例化后的記錄對象寫入到指定的緩存對象中,這篇文章主要介紹了rust 一個(gè)日志緩存記錄的通用實(shí)現(xiàn)方法,需要的朋友可以參考下2024-04-04
Rust調(diào)用函數(shù)操作符?.?和?::?的區(qū)別詳解
在Rust中,.和::操作符都可以用來調(diào)用方法,但它們的用法有所不同,所以本文就將詳細(xì)的給大家介紹一下.和::操作符的區(qū)別,感興趣的同學(xué)跟著小編一起來學(xué)習(xí)吧2023-07-07
Rust?編程語言中的所有權(quán)ownership詳解
這篇文章主要介紹了Rust?編程語言中的所有權(quán)ownership詳解的相關(guān)資料,需要的朋友可以參考下2023-02-02
如何基于Rust實(shí)現(xiàn)文本搜索minigrep
這篇文章主要介紹了基于Rust實(shí)現(xiàn)的文本搜索minigrep,本次演示介紹針對原作者代碼程序的查詢邏輯做了一點(diǎn)點(diǎn)小的優(yōu)化,原程序邏輯的查詢是放在了程序運(yùn)行的時(shí)候,邏輯修改后啟動(dòng)的時(shí)候可以添加參數(shù),也可以啟動(dòng)后添加,需要的朋友可以參考下2024-08-08

