1. 概述

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

一切权在大多数情况下是比较明晰的,关于一个值,咱们一般能准确断定哪个变量拥有它,但在某些场景中,单个值有或许被多个一切者所持有。

咱们看一个图:

49_Rc

图中是一个图的数据结构,节点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的一切权,如下图:

49_Rc

为了完成上面的功能,咱们编写如下代码:

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>,将处理该问题,如下示例代码:

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函数

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函数打印出强引证的计数,运行结果如下

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