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

相关推荐
大莲芒2 小时前
react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析--react17
前端·react.js·前端框架
木木黄木木4 小时前
html5炫酷3D文字效果项目开发实践
前端·3d·html5
Li_Ning215 小时前
【接口重复请求】axios通过AbortController解决页面切换过快,接口重复请求问题
前端
胡八一5 小时前
Window调试 ios 的 Safari 浏览器
前端·ios·safari
Dontla5 小时前
前端页面鼠标移动监控(鼠标运动、鼠标监控)鼠标节流处理、throttle、限制触发频率(setTimeout、clearInterval)
前端·javascript
再学一点就睡5 小时前
深拷贝与浅拷贝:代码世界里的永恒与瞬间
前端·javascript
CrimsonHu6 小时前
B站首页的 Banner 这么好看,我用原生 JS + 三大框架统统给你复刻一遍!
前端·javascript·css
Enti7c6 小时前
前端表单输入框验证
前端·javascript·jquery
拉不动的猪6 小时前
几种比较实用的指令举例
前端·javascript·面试
麻芝汤圆6 小时前
MapReduce 的广泛应用:从数据处理到智能决策
java·开发语言·前端·hadoop·后端·servlet·mapreduce