Rust_smart_pointer
智能指针
智能指针是一类数据结构,它们表现类似指针,但是也拥有额外的元数据和功能。
- 引用计数 ( reference counting )智能指针类型。这种指针允许数据有多个所有者,它会记录所有者的数量,当没有所有者时清理数据。
- 在 Rust 中因为引用和借用,普通引用和智能指针的一个额外的区别是引用是一类 只借用数据的指针 ;相反,在大部分情况下,智能指针 拥有 它们指向的数据。
常用的智能指针:
Box<T>
,用于在堆上分配值Rc<T>
,一个引用计数类型,其数据可以有多个所有者Ref<T>
和RefMut<T>
,通过RefCell<T>
访问。(RefCell<T>
是一个在运行时而不是在编译时执行借用规则的类型)。
使用Box <T>
指向堆上的数据
box 允许你将一个值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。
使用Box <T>
在堆上储存数据
let b = Box::new(5);
Box允许创建递归类型
递归类型 ( recursive type )的值可以拥有另一个同类型的值作为其自身的一部分。
- cons list:
- cons list 是一个来源于 Lisp 编程语言及其方言的数据结构,它由嵌套的列表组成。(类似于c++的广义表)
- Box是指针,是可以知道大小的。
1 |
|
通过Deref trait将智能指针当作常规引用处理
- 实现
Deref
trait 允许我们重载 解引用运算符 - 解引用
1 |
|
- 像引用一样使用Box
<T>
1 |
|
- 将
y
设置为一个指向x
值拷贝的Box<T>
实例,而不是指向x
值的引用。
Rc <T>
引用计数智能指针
如果某个值有零个引用,就代表没有任何有效引用并可以被清理。
- 使用Rc
<T>
共享数据
1 |
|
也可以调用 a.clone()
而不是 Rc::clone(&a)
,不过在这里 Rust 的习惯是使用 Rc::clone
。
- 克隆Rc
<T>
会增加引用计数
Rc::clone
:
Rc::clone(&sun)
不会复制Sun
实例,而是增加Rc
的引用计数。- 所有行星共享同一个
Sun
实例。
- 引用计数 :
- 每次调用
Rc::clone(&sun)
,引用计数加 1。 - 每次调用
drop
,引用计数减 1。
drop
的作用 :
drop
用于手动释放所有权,减少引用计数。- 当引用计数为 0 时,
Rc
会自动释放内存。
cow:
Cow
的核心思想是:
- 如果数据是 只读的 ,直接 使用借用 (
Borrowed
),避免复制。 - 如果数据 需要修改 ,则 复制数据并拥有所有权 (
Owned
)。
Cow
的常用方法
(1) to_mut
:获取可变引用
如果 Cow
是 Borrowed
,则复制数据并转换为 Owned
;如果已经是 Owned
,则直接返回可变引用。
1 |
|
(2) into_owned
:获取所有权
将 Cow
转换为拥有所有权的数据。
1 |
|
(3) is_borrowed
和 is_owned
:检查状态
is_borrowed
:检查是否是Borrowed
。is_owned
:检查是否是Owned
。
1 |
|
使用Drop Trait运行清理代码
定义
1 |
|
drop方法在值离开作用域时自动调用
也可以手动调用drop()来清理代码
1 |
|
Drop与所有权
- 当一个值的所有权转移时,drop不会立即调用,只有在值离开作用域时才会调用
- 如果值被移动(如传递给函数),drop会在新作用域结束时调用
Drop与Copy
二者不能同时实现
Copy
trait 表示类型可以通过位复制来复制值,而Drop
表示类型需要清理操作。- 如果类型实现了
Drop
,Rust 会阻止你实现Copy
,因为复制可能会导致资源被多次释放。
RefCell <T>
和内部可变模式
- 内部可变性是rust中的一个设计模式,它允许你 即使在有不可变引用时也可以改变数据 。
- 该模式在数据结构中使用
unsafe
代码来模糊rust通常的可变性和借用规则。不安全代码表明我们在手动检查这些规则而不是让编译器替我们检查。
通过 RefCell<T>
在运行时检查借用规则
- 不同于
Rc<T>
,RefCell<T>
代表其数据的唯一的所有权。
如下为选择 Box<T>
,Rc<T>
或 RefCell<T>
的理由:
Rc<T>
允许相同数据 有多个所有者 ;Box<T>
和RefCell<T>
有单一所有者。Box<T>
允许在编译时执行不可变或可变借用检查;Rc<T>
仅允许在编译时执行不可变借用检查;RefCell<T>
允许在运行时执行不可变或可变借用检查。- 因为
RefCell<T>
允许在运行时执行可变借用检查,所以我们可以在即便RefCell<T>
自身是不可变的情况下修改其内部的值。
内部可变性:不可变值的可变借用
借用规则的一个推论是当有一个不可变值时,不能可变地借用它。
- mock对象
- mock 对象 是特定类型的 测试替身 ,它们记录测试过程中发生了什么以便可以断言操作是正确的。
主要方法
borrow()
:获取一个不可变引用(Ref<T>
)。如果已经有可变引用,会 panic。borrow_mut()
:获取一个可变引用(RefMut<T>
)。如果已经有其他引用(可变或不可变),会 panic。try_borrow()
和try_borrow_mut()
:与borrow()
和borrow_mut()
类似,但不会 panic,而是返回Result
。
Arc <T>
: Atomic Reference Counting (原子引用计数)
- 引用计数 :
Arc<T>
会跟踪指向同一数据的引用数量。每次克隆Arc<T>
时,引用计数会增加;当Arc<T>
被丢弃时,引用计数会减少。当引用计数为 0 时,数据会被释放。 - 线程安全 :
Arc<T>
是线程安全的,因为它的引用计数操作是原子的(atomic),这意味着多个线程可以安全地共享同一个Arc<T>
。 - 不可变性 :
Arc<T>
本身是不可变的。如果需要修改内部数据,通常需要与Mutex<T>
或RwLock<T>
结合使用。
主要方法
new(value: T) -> Arc<T>
:创建一个新的Arc<T>
。clone(&self) -> Arc<T>
:克隆Arc<T>
,增加引用计数。strong_count(&self) -> usize
:返回当前 强引用计数 。downgrade(&self) -> Weak<T>
:创建一个 弱引用 (Weak<T>
),不会增加引用计数。try_unwrap(this: Arc<T>) -> Result<T, Arc<T>>
:如果引用计数为 1,则返回内部数据;否则返回Arc<T>
。
Rust_smart_pointer
https://pqcu77.github.io/2025/02/19/Rust-smart-pointer/