查看: 5|回复: 0

[教程] Rust 智能指针的使用详解

[复制链接]

3

主题

0

回帖

0

积分

积极分子

金币
0
阅读权限
220
精华
0
威望
0
贡献
0
在线时间
0 小时
注册时间
2008-4-11
发表于 2025-9-24 11:51:50 | 显示全部楼层 |阅读模式

一、Rust 智能指针详解

智能指针是Rust中管理内存和所有权的核心工具,通过封装指针并添加元数据(如引用计数)来实现安全的内存管理。以下是主要类型及其原理、使用场景和示例:

1、Box<T>:堆内存分配

  • 原理:在堆上分配内存,栈中存储指向堆的指针。所有权唯一,离开作用域时自动释放内存。
  • 使用场景
    • 编译时未知大小的类型(如递归类型)
    • 转移大数据所有权避免拷贝
    • 特质对象(Trait Object)的动态分发
  • 示例
    fn main() {
        let b = Box::new(5); // 在堆上存储整数5
        println!("b = {}", b); // 输出: b = 5
    } // 离开作用域,堆内存自动释放
    

2、Rc<T>:引用计数指针

  • 原理:通过引用计数跟踪值的所有者数量。当计数归零时自动释放内存。仅适用于单线程
  • 使用场景:多个部分只读共享数据(如图结构、共享配置)。
  • 示例
    use std::rc::Rc;
    fn main() {
        let a = Rc::new(10);
        let b = Rc::clone(&a); // 引用计数+1
        println!("Count: {}", Rc::strong_count(&a)); // 输出: Count: 2
    } // 离开作用域,计数归零,内存释放
    

3、RefCell<T>:内部可变性

  • 原理:在运行时检查借用规则(而非编译时),允许通过不可变引用修改内部数据。使用borrow()borrow_mut()访问。
  • 使用场景:需要修改只读共享数据时(如缓存更新)。
  • 示例
    use std::cell::RefCell;
    fn main() {
        let c = RefCell::new(42);
        *c.borrow_mut() += 1; // 运行时借用检查
        println!("c = {}", c.borrow()); // 输出: c = 43
    }
    

4、Arc<T>:原子引用计数

  • 原理:类似Rc<T>,但使用原子操作保证线程安全。性能略低于Rc
  • 使用场景:多线程共享数据(需配合Mutex)。
  • 示例
    use std::sync::Arc;
    use std::thread;
    fn main() {
        let val = Arc::new(100);
        let handle = thread::spawn(move || {
            println!("Thread: {}", val); // 安全共享
        });
        handle.join().unwrap();
    }
    

5、Mutex<T>与RwLock<T>:线程同步

  • Mutex<T>:互斥锁,一次仅允许一个线程访问数据。
    use std::sync::{Arc, Mutex};
    use std::thread;
    fn main() {
        let counter = Arc::new(Mutex::new(0));
        let mut handles = vec![];
        for _ in 0..10 {
            let c = Arc::clone(&counter);
            let handle = thread::spawn(move || {
                let mut num = c.lock().unwrap();
                *num += 1; // 修改受保护数据
            });
            handles.push(handle);
        }
        for handle in handles { handle.join().unwrap(); }
        println!("Result: {}", *counter.lock().unwrap()); // 输出: Result: 10
    }
    
  • RwLock<T>:读写锁,允许多个读取或单个写入。
    use std::sync::RwLock;
    let lock = RwLock::new(5);
    {
        let r1 = lock.read().unwrap(); // 多个读取并发
        let r2 = lock.read().unwrap();
    }
    {
        let mut w = lock.write().unwrap(); // 独占写入
        *w += 1;
    }
    

6、Weak<T>:解决循环引用

  • 原理:Weak 是 Rc 的非拥有智能指针,它不增加引用计数。
  • 使用场景:用于解决循环引用问题
  • 示例
    use std::rc::{Rc, Weak};
    use std::cell::RefCell;
    
    // 定义节点结构
    struct Node {
        value: i32,
        parent: RefCell<Weak<Node>>, // 使用 Weak 避免循环引用
        children: RefCell<Vec<Rc<Node>>>, // 子节点使用 Rc
    }
    
    fn main() {
        // 创建叶子节点
        let leaf = Rc::new(Node {
            value: 3,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![]),
        });
    
        // 创建分支节点,并设置 leaf 为其子节点
        let branch = Rc::new(Node {
            value: 5,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![Rc::clone(&leaf)]),
        });
    
        // 设置 leaf 的父节点为 branch(使用 downgrade 创建弱引用)
        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
    
        // 尝试升级 leaf 的父节点
        if let Some(parent) = leaf.parent.borrow().upgrade() {
            println!("Leaf 的父节点值: {}", parent.value); // 输出: Leaf 的父节点值: 5
        } else {
            println!("父节点已被释放");
        }
    
        // 当 branch 被丢弃时,leaf 的 parent 升级会失败
    }
    
    

7、组合模式

  • Rc<RefCell<T>>:单线程内共享可变数据。
    let shared_data = Rc::new(RefCell::new(vec![1, 2]));
    shared_data.borrow_mut().push(3); // 修改共享数据
    
  • Arc<Mutex<T>>:多线程共享可变数据(最常见组合)。
    let data = Arc::new(Mutex::new(0));
    // 多线程修改数据(见上文Mutex示例)
    

8、对比总结

类型线程安全可变性适用场景
Box<T>所有权唯一堆分配、递归类型
Rc<T>不可变共享单线程共享只读数据
RefCell<T>内部可变单线程运行时借用检查
Arc<T>不可变共享多线程共享只读数据
Mutex<T>线程安全可变多线程互斥修改数据
RwLock<T>读写分离读多写少场景

二、Rust 智能指针示例

以下是一个复杂的 Rust 智能指针示例,结合了 RcRefCell 和自定义智能指针,模拟图形渲染场景中的资源管理:

use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::ops::Deref;

// 自定义智能指针:带引用计数的纹理资源
struct Texture {
    id: u32,
    data: Vec<u8>,
}

impl Texture {
    fn new(id: u32, size: usize) -> Self {
        Texture {
            id,
            data: vec![0; size],
        }
    }
}

// 自定义智能指针:TextureHandle
struct TextureHandle(Rc<Texture>);

impl TextureHandle {
    fn new(texture: Texture) -> Self {
        TextureHandle(Rc::new(texture))
    }
    
    fn get_id(&self) -> u32 {
        self.0.id
    }
}

impl Deref for TextureHandle {
    type Target = Texture;
    
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// 场景节点:支持父子关系
struct SceneNode {
    name: String,
    texture: Option<TextureHandle>,
    children: RefCell<Vec<Rc<RefCell<SceneNode>>>>,
    parent: RefCell<Weak<RefCell<SceneNode>>>,
}

impl SceneNode {
    fn new(name: &str) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(SceneNode {
            name: name.to_string(),
            texture: None,
            children: RefCell::new(Vec::new()),
            parent: RefCell::new(Weak::new()),
        }))
    }
    
    fn add_child(parent: &Rc<RefCell<SceneNode>>, child: &Rc<RefCell<SceneNode>>) {
        child.borrow_mut().parent.replace(Rc::downgrade(parent));
        parent.borrow_mut().children.borrow_mut().push(Rc::clone(child));
    }
    
    fn set_texture(&mut self, texture: TextureHandle) {
        self.texture = Some(texture);
    }
    
    fn print_tree(&self, depth: usize) {
        let indent = "  ".repeat(depth);
        println!("{}{}", indent, self.name);
        if let Some(tex) = &self.texture {
            println!("{}Texture ID: {}", indent, tex.get_id());
        }
        for child in self.children.borrow().iter() {
            child.borrow().print_tree(depth + 1);
        }
    }
}

fn main() {
    // 创建共享纹理资源
    let shared_texture = TextureHandle::new(Texture::new(101, 1024));
    
    // 创建场景节点
    let root = SceneNode::new("Root");
    let camera = SceneNode::new("Camera");
    let mesh1 = SceneNode::new("Mesh1");
    let mesh2 = SceneNode::new("Mesh2");
    
    // 设置纹理
    {
        let mut root_mut = root.borrow_mut();
        root_mut.set_texture(shared_texture);
    }
    
    // 构建场景层级
    SceneNode::add_child(&root, &camera);
    SceneNode::add_child(&root, &mesh1);
    SceneNode::add_child(&mesh1, &mesh2);
    
    // 打印场景树
    root.borrow().print_tree(0);
    
    // 验证引用计数
    println!("\nReference counts:");
    println!("Root strong: {}", Rc::strong_count(&root));
    println!("Root weak: {}", Rc::weak_count(&root));
}

示例解析:

  1. 自定义智能指针 TextureHandle

    • 包装 Rc<Texture> 实现资源共享
    • 实现 Deref 获得透明访问
    • 提供资源 ID 访问方法
  2. 场景图管理 SceneNode

    • 使用 Rc<RefCell<SceneNode>> 实现共享所有权和内部可变性
    • 子节点列表:RefCell<Vec<Rc<...>>> 实现运行时可变借用
    • 父节点:RefCell<Weak<...>> 避免循环引用
  3. 资源共享机制

    • 纹理资源通过 TextureHandle 共享
    • 节点树通过 Rc 共享所有权
    • 使用 Weak 引用打破循环依赖
  4. 输出示例

Root
Texture ID: 101
  Camera
  Mesh1
    Mesh2

Reference counts:
Root strong: 1
Root weak: 2
PS G:\Learning\Rust\ttt> 

关键特性:

  1. 内存安全:自动管理资源释放
  2. 内部可变性:通过 RefCell 修改不可变引用
  3. 循环引用防护:Weak 指针避免内存泄漏
  4. 透明访问:通过 Deref 实现直接访问
  5. 运行时借用检查:RefCell 在运行时验证借用规则

到此这篇关于Rust 智能指针的使用详解的文章就介绍到这了,更多相关Rust 智能指针内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!

您可能感兴趣的文章:
  • rust中智能指针的实现
  • 解读Rust的Rc<T>:实现多所有权的智能指针方式
  • Rust之智能指针的用法
  • Rust 智能指针实现方法
  • rust智能指针的具体使用
  • 详解rust 自动化测试、迭代器与闭包、智能指针、无畏并发
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部