Rust 智能指针的实现

 更新时间:2026年06月24日 08:17:44   作者:菜鸟谢  
本文主要介绍了Rust智能指针的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

涵盖:Box、Rc/Arc、RefCell/Mutex、Deref/Drop trait、循环引用等
重点:智能指针选型、线程安全约束、内部可变性、内存泄漏预防

一、基础概念

1. 普通裸指针*const T / *mut T

无所有权、无自动内存管理、无借用检查,仅存内存地址,操作必须 unsafe

2. 智能指针本质

实现了 Deref + Drop trait 的结构体

  1. Deref:自动解引用,像普通引用一样使用;
  2. Drop:离开作用域自动释放堆内存,防止内存泄漏;
  3. 变量本体存在栈,栈内只存指针/元数据,真实数据存堆;
  4. 自带所有权、借用、计数、锁等安全逻辑,不用手动管理内存。

区分普通引用 & 智能指针

类型说明
&T借用,不拥有数据,生命周期依附所有者
智能指针拥有堆数据所有权,自行管理内存生命周期

二、核心智能指针分类

独占所有权:Box

底层结构

struct Box<T>(*mut T);

栈上仅一根堆指针,T 完整分配在堆。

核心特性

  1. 唯一所有者,同一时间只能有一个 Box;
  2. 无额外开销,性能等同于裸指针;
  3. 自动 Drop,出作用域释放堆;
  4. 实现 Deref<Target=T>,可直接 . 访问字段。

适用场景

  1. 递归结构体/枚举(解决无限尺寸)
struct Node {
    val: i32,
    next: Option<Box<Node>>
}
  1. 超大栈值,避免栈溢出,转移到堆;
  2. 构造 trait 对象 Box<dyn Trait>(胖指针:数据指针 + vtable);
  3. FFI、需要堆分配的场景。

示例

let b = Box::new(10);
println!("{}", *b); // Deref 解引用
println!("{}", b);  // 自动隐式解引用

单线程共享所有权:Rc / Weak

Rc 底层

堆上分配 RcInner<T>,包含:

  • 强引用计数 strong:持有数据的 Rc 数量;
  • 弱引用计数 weak:Weak 数量;
  • 真实数据 T。

规则

  1. Rc<T> 多份克隆共享同一堆数据,单线程专用,不支持多线程
  2. 每次 .clone() 仅栈拷贝指针,堆上 strong+1;
  3. 所有 Rc 销毁 strong=0,释放数据;weak 不阻止释放;
  4. 内部不可变:Rc 只读,如需修改搭配 RefCell<T>

Weak 弱指针

  1. 不增加强计数,不阻止数据释放;
  2. 调用 upgrade() 尝试转为 Option<Rc<T>>,数据已销毁返回 None;
  3. 解决循环引用内存泄漏。

示例

use std::rc::Rc;
let a = Rc::new("hello".to_string());
let b = Rc::clone(&a); // 仅计数 +1,不拷贝字符串
println!("strong count: {}", Rc::strong_count(&a));

多线程共享所有权:Arc / Weak

和 Rc 逻辑完全一致,区别:

特性RcArc
计数方式普通整数原子操作 AtomicUsize
线程安全❌ 单线程✅ 跨线程
性能开销略高(原子指令)

搭配内部可变性:Arc<Mutex<T>> / Arc<RwLock<T>>

内部可变性容器(栈存指针,堆存标记 + 数据)

1. RefCell 单线程内部可变性

突破外部不可变限制,运行时借用检查(而非编译期)

  • 存储:堆存数据 + 借用状态标记(借出数量、是否可变)
  • 方法:
    • .borrow()Ref<T> 不可变借用(可多个同时存在)
    • .borrow_mut()RefMut<T> 可变借用(同一时间只能一个)
  • 违反规则运行时 panic,非编译报错。

搭配 Rc 实现单线程共享可变数据:Rc<RefCell<T>>

use std::rc::Rc;
use std::cell::RefCell;
let val = Rc::new(RefCell::new(0));
*val.borrow_mut() += 1;

2. Cell

内部可变性,只适合 Copy 小类型

  • 小 Copy 类型直接栈存储,无堆分配;
  • 无借用,直接 .get() / .set(),不会触发 panic;
  • 不能包裹 String、Vec 非 Copy 类型。

3. Mutex 互斥锁(多线程)

多线程内部可变性,同一时间仅一个线程访问数据:

  • .lock() 获取锁,返回 MutexGuard<T>,阻塞等待;
  • 中毒:持有锁线程 panic,锁永久失效;
  • 搭配 Arc:Arc<Mutex<T>> 多线程共享可变资源。

4. RwLock 读写锁

  • 多个读共存,写独占;读多写少场景性能优于 Mutex。

三、Deref 自动解引用(智能指针核心 trait)

Deref trait 定义

trait Deref {
    type Target;
    fn deref(&self) -> &Self::Target;
}

实现后支持:

  1. *智能指针 手动解引用;
  2. 自动隐式解引用,直接调用内部字段/方法;
  3. 解引用强制转换:函数传参自动逐层解引用。

示例:Box 自动解引用

struct Point { x: i32, y: i32 }
let p = Box::new(Point { x: 1, y: 2 });
println!("{}", p.x); // 自动 deref,无需 *p

DerefMut 可变解引用

trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Box<T>RefMutMutexGuard 均实现,支持 *mut_ptr = xxx 修改。

四、Drop 析构 trait

所有所有权智能指针都实现 Drop,离开作用域自动执行:

智能指针Drop 行为
Box释放堆上 T
Rc/Arcstrong 计数 -1,计数归零释放堆
RefCell/Mutex释放内部堆数据

手动提前释放:std::mem::drop(x),主动转移所有权触发 Drop。

五、智能指针选型对照表

指针线程安全所有权模式可变性方案典型用途
Box单/多线程均可独占唯一外部 mut递归类型、堆分配、trait 对象
Rc❌ 单线程共享多所有者Rc<RefCell>单线程多层共享
Arc✅ 多线程共享多所有者Arc<Mutex/RwLock>跨线程共享资源
RefCell❌ 单线程独占运行时借用检查单线程内部可变性
Mutex✅ 多线程独占互斥排他访问多线程可变共享
RwLock✅ 多线程独占读共享写排他读多写少并发
Weak跟随 Rc/Arc弱引用不计数解决循环引用

六、关键使用规则

1. 所有权与拷贝

指针行为
Boxmove 语义,赋值转移所有权;clone 深拷贝堆数据
Rc/Arc.clone() 仅增加计数,零堆拷贝
Weak不能直接使用,必须 upgrade 转 Rc/Arc

2. 循环引用泄漏问题

单纯 Rc/Arc 双向引用会造成计数永远不为 0,内存泄漏;

解决:其中一端改用 Weak 弱指针。

3. 线程安全约束 Send / Sync

指针Send/Sync
BoxT 满足 Send/Sync 则 Box 满足
Rc不实现 Send/Sync,禁止跨线程
Arc实现 Send+Sync,可跨线程
MutexSend+Sync

4. 解引用强制多态(函数传参自动转换)

fn read(s: &str) {}
let b = Box::new("test".to_string());
read(&b);
// &Box<String> → &String → &str 自动多层 deref

七、高频易错点

⚠️ 以下易错点需特别注意

  1. Rc 传给 spawn 线程直接编译报错,必须换 Arc;
  2. RefCell 在同一作用域同时存在可变 + 不可变借用,运行时 panic;
  3. 忘记循环引用搭配 Weak,长期运行内存持续上涨;
  4. Mutex lock() 在线程 panic 后锁中毒,后续全部阻塞;
  5. Box::new 超大数组会先在栈构造再拷贝堆,超大数组优先 Vec;
  6. 混淆借用 &T 和智能指针:&无所有权,智能指针拥有堆内存;
  7. Cell 仅支持 Copy 类型,包裹 String 编译报错。

八、速记口诀

智能指针实现 Deref+Drop,栈存指针堆存数据;
Box 独占单所有者,递归 trait 对象必备;
Rc 单线程共享,Arc 多线程原子计数;
Weak 弱引用防循环泄漏,upgrade 转为强指针;
RefCell 单线程内部可变,运行借用检查;
Mutex/RwLock 多线程锁,读写分离性能更佳;
Deref 自动解引用,点号直接访问内部字段;
Rc 禁止跨线程,跨线程共享只用 Arc。

到此这篇关于Rust 智能指针的实现的文章就介绍到这了,更多相关Rust 智能指针内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Rust操作Redis从入门到生产级应用

    Rust操作Redis从入门到生产级应用

    本文将基于主流的redis-rs库,带你全面掌握Rust操作Redis的技巧,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-05-05
  • rust 中生成与使用protobuf的方法

    rust 中生成与使用protobuf的方法

    这篇文章主要介绍了rust中protobuf生成与使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • 探索Rust切片与Go有何区别

    探索Rust切片与Go有何区别

    这篇文章主要为大家介绍了Rust切片与Go的区别探索,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 深入剖析Rust 中的 Move、Copy 和 Clone

    深入剖析Rust 中的 Move、Copy 和 Clone

    在 Rust 编程语言中,move、Copy 和 Clone 是所有权系统的核心概念,它们深刻影响着数据的传递和复制方式,这些机制不仅确保了内存安全,还提供了高效的性能优化手段,本文将深入探讨这些概念的内部机制、使用场景、性能影响以及高级用法,感兴趣的朋友一起看看吧
    2025-05-05
  • Rust中的Cargo构建、运行、调试

    Rust中的Cargo构建、运行、调试

    Cargo是rustup安装后自带的,Cargo 是 Rust 的构建系统和包管理器,这篇文章主要介绍了Rust之Cargo构建、运行、调试,需要的朋友可以参考下
    2022-09-09
  • Rust中实例化动态对象的示例详解

    Rust中实例化动态对象的示例详解

    这篇文章主要为大家详细介绍了Rust中实例化动态对象的多种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-02-02
  • 使用环境变量实现Rust程序中的不区分大小写搜索方式

    使用环境变量实现Rust程序中的不区分大小写搜索方式

    本文介绍了如何在Rust中实现不区分大小写的搜索功能,并通过测试驱动开发(TDD)方法逐步实现该功能,通过修改运行函数和获取环境变量,程序可以根据环境变量控制搜索模式
    2025-02-02
  • rust流程控制的具体使用

    rust流程控制的具体使用

    在Rust中,控制流包括条件语句、循环和匹配模式等,用于实现程序的逻辑和流程控制,本文就来详细的介绍一下,感兴趣的可以了解一下
    2023-12-12
  • Rust中into和from用法及区别介绍

    Rust中into和from用法及区别介绍

    这篇文章主要介绍了Rust中的 into和from使用及区别介绍,into和from是Rust语言中两个用于类型转换的函数,它们分别属于Into和From这两个trait,本文通过实例代码详细讲解,需要的朋友可以参考下
    2023-04-04
  • Rust中自定义Debug调试输出的示例详解

    Rust中自定义Debug调试输出的示例详解

    这篇文章主要介绍了Rust中自定义Debug调试输出的示例详解,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-12-12

最新评论