Rust智能指针遵循RAII原则管理内存
- RAII原则:RAII(Resource Acquisition Is Initialization)即资源获取即初始化,在Rust中,当一个对象(如智能指针)进入其作用域时,会分配资源(如内存),当对象离开作用域时,会自动释放其持有的资源。
Box
遵循RAII:Box
是简单的堆分配智能指针。当Box
创建时,在堆上分配内存存储数据,Box
离开作用域时,其析构函数被调用,释放堆上的数据。
Rc
遵循RAII:Rc
(引用计数)通过引用计数管理资源。创建Rc
时,引用计数设为1。每次克隆Rc
,引用计数增加1。当Rc
离开作用域,引用计数减少1。当引用计数变为0,Rc
持有的资源被释放。
Rc
详细的RAII过程
- 引用计数增加:当使用
clone
方法克隆Rc
时,引用计数增加。例如:
use std::rc::Rc;
let a = Rc::new(5);
let b = a.clone();
// 此时a和b指向的资源引用计数为2
- 引用计数减少:当
Rc
离开作用域,其析构函数被调用,引用计数减少。例如:
{
let a = Rc::new(5);
{
let b = a.clone();
} // b离开作用域,引用计数减1
} // a离开作用域,引用计数减为0,资源释放
- 最终释放资源:当引用计数变为0,
Rc
的析构函数释放其所持有的资源,自动调用资源的析构函数释放内存等资源。
循环引用对RAII的影响及解决
- 影响:循环引用会导致引用计数永远不会降为0,资源无法释放,造成内存泄漏。例如:
use std::rc::Rc;
struct Node {
value: i32,
next: Option<Rc<Node>>,
}
let a = Rc::new(Node { value: 1, next: None });
let b = Rc::new(Node { value: 2, next: Some(a.clone()) });
a.next = Some(b.clone());
// a和b形成循环引用,即使a和b离开作用域,引用计数也不会为0
- 解决:
- 使用
Weak
指针:Weak
指针是Rc
的弱引用,不增加引用计数。当强引用(Rc
)全部消失,Weak
指针指向的资源会被释放。例如:
use std::rc::{Rc, Weak};
struct Node {
value: i32,
next: Option<Weak<Node>>,
}
let a = Rc::new(Node { value: 1, next: None });
let b = Rc::new(Node { value: 2, next: Some(Rc::downgrade(&a)) });
a.next = Some(Rc::downgrade(&b));
// 这里通过Weak指针避免了循环引用
- **手动打破循环**:在代码逻辑中,在合适的时机手动断开循环引用关系,使引用计数能降为0从而释放资源。