49_Rc<T>

1. 概述

Rc<T>是一个引用计数智能指针。

所有权在大多数情况下是比较清晰的,对于一个值,我们一般能准确判定哪个变量拥有它,但在某些场景中,单个值有可能被多个所有者所持有。

我们看一个图:

图中是一个图的数据结构,节点6被多个数据节点引用,就可以说节点6同时被多个数据拥有。

为了支持多重引用,rust提供了一种叫Rc<T>的数据类型。即reference counting(引用计数)。这个数据类型会在实例的内部维护一个用于记录值的引用次数的计数器。从而判断该值是否仍然在使用,得以追踪到所有值的引用。如果记录到某个值的引用个数为0,那么可以意味该值可以被清理掉,而且不用出发引用失效的问题。

2. Rc使用场景

需要在heap上分配数据,这些数据被程序员的多个部分读取(只读),但在编译时无法确定哪个部分最后使用完这些数据,就可以使用Rc<T>。需要注意的是,Rc<T>只能用于在线程场景。

Rc<T>不在预导入模块(prelude)中,Rc::clone(&a)函数用于增加引用计数,而Rc::strong_count(&a)用于获得引用计数,而Rc::weak_count函数用于弱引用计数。

下面我们先看一个示例: 两个List共享另一个List的所有权,如下图:

为了实现上面的功能,我们编写如下代码:

rust 复制代码
enum List {
    Cons(i32, Box<List>),
    Nil
}

use crate::List:: {Cons, Nil};

fn main() {
    let a = Cons(5,
        Box::new(Cons(10,
           Box::new(Nil))));

    // 开头元素是3,再接上a
    let b = Cons(3, Box::new(a));
    // 开头元素是4,再接上a
    let c = Cons(4, Box::new(a));
}

不过以上的代码编译时会发生错误,因为a的已经将值移动到了b即将所有权转移到了b,如果再移动到c将会发生报错。如果我门使用Rc<T>,将解决该问题,如下示例代码:

rust 复制代码
use crate::List::{Cons, Nil};
use std::rc::Rc;

enum List {
    Cons(i32, Rc<List>),
    Nil,
}

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10,Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(3, Rc::clone(&a));
}

在上面的代码中,当创建了ba的引用时,Rc计数器变成2,当创建ca的引用时,Rc计数器变成3,abc这3个应用共享的时一份数据。每次调用Rc::clone方法,Rc的计数器都会增加1。Rc<T>这种数据类型的引用计数器减少到0的时候,对应的数据才会被清理掉。

其实在上面的示例代码中,如果我想拷贝a对应数据,也可以a.clone()方法,但是该方法会执行深度拷贝,在heap上创建出另外一份数据。而Rc::clone只会进行浅拷贝,在stack上创建数据的引用。我们通常惯例调用Rc::clone而不是调用数据类型对应的clone方法。

下面我们修改一下main函数

rust 复制代码
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("counter after creating a = {}", Rc::strong_count(&a));

let b = Cons(3, Rc::clone(&a));
println!("counter after creating b = {}", Rc::strong_count(&a));

{
    let c = Cons(3, Rc::clone(&a));
    println!("counter after creating c = {}", Rc::strong_count(&a));
}

println!(
    "counter after c goes out of scopes = {}",
    Rc::strong_count(&a)
);

我们使用Rc::strong_count函数打印出强引用的计数,运行结果如下

rust 复制代码
counter after creating a = 1
counter after creating b = 2
counter after creating c = 3
counter after c goes out of scopes = 2

可以看到,当变量离开作用域的时候,计数引用也会自动减少。

3. Rc::clone()和类型的clone()方法

Rc::clone(): 增加引用,不会执行数据的深度拷贝操作 类型的clone(): 大多情况会执行数据的深度拷贝操作

Rc<T>通过不可变引用,使得我们可以在程序不同部分之间共享只读数据。但是,在开发中很多时候需要数据可变,如何允许数据变化呢?我们将在后续的章节中介绍。

相关推荐
计算机魔术师3 分钟前
OpenAI 宣称破解千禧年难题,数学家却说是「反例搜索」?
前端
志尊宝4 分钟前
Vue3 零基础每日笔记(015):条件渲染 v-if 与 v-show——控制 DOM 生死还是控制显隐
前端·vue.js·笔记
掘金者阿豪11 分钟前
OceanBase 和金仓怎么选?别只看分布式,复杂查询更考验架构取舍
前端·后端·架构
Bug修理工Bubble17 分钟前
Swift ARC:一个对象到底什么时候才会被释放?
前端
ClouGence22 分钟前
GPT-6 做 UI 自动化测试:Demo 惊艳,但真的适合长期回归吗?
前端·chatgpt·测试
孙启超29 分钟前
【AI开发之Rust】第 1 课:认识 Rust 与开发环境 —— 装好工具链,跑通第一个程序
开发语言·人工智能·后端·ai·rust·安全架构
阿酷tony1 小时前
视频专栏列表的防录屏水印和跑马灯效果(也可网站调用)
java·前端·音视频
凌风的跨境分享1 小时前
Temu店群运维提效:定时策略自动化任务全场景实操指南
大数据·运维·前端·人工智能·架构·自动化
linux_cfan2 小时前
videojs v10 源代码系列解读:06 · DOM 工具箱:事件、聚焦、Shadow DOM、定位
前端
尾善爱看海2 小时前
Vue 面试进阶篇:Composition API、插槽、自定义指令……8 个章节 + 高频面试题全解析
前端·javascript·vue.js·面试·vue