xxzxka

Rust智能指针之Rc<T>(引用计数)指针


前言

大多数情况可以很清晰地知晓变量的所有权,如某个变量拥有某个值。某些复杂的数据结构如:图结构,一个图节点可能存在多条边指向它,概念上这些边都是这个点的“拥有者”。为了启用多所有权,Rust 有一个叫做 Rc<T> 的类型。其名称为 引用计数reference counting)的缩写。引用计数意味着记录一个值引用的数量来知晓这个值是否仍在被使用。如果某个值有零个引用,就代表没有任何有效引用并可以被清理。

1. Rc在堆上的应用

Rc用于分配在堆上的数据被程序多个部分引用,且无法在编译时确定程序的哪一部分会最后结束使用它的情况。倘若能知晓哪部分最后结束使用,正常的所有权系统就可以工作了。

注意:Rc只能在单线程程序中使用

2. 使用Rc共享数据

实现两个列表共享另一个列表所有权的结构,概览图如下:

尝试使用Box实现:

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

fn main() {
    let a = List::Cons(5, Box::new(List::Cons(10, Box::new(List::Nil)))); //a
    let b = List::Cons(3,Box::new(a));
    let c = List::COns(4,Box::new(a));
}

cargo build编译后收到控制台报错:

error[E0382]: use of moved value: `a`
  --> src\main.rs:39:29
   |
37 |     let a =Cons(5,Box::new(Cons(10,Box::new(Nil))));
   |         - move occurs because `a` has type `List`, which does not implement the `Copy` trait
38 |     let b = Cons(3,Box::new(a));
   |                             - value moved here
39 |     let c = Cons(4,Box::new(a));
   |                             ^ value used here after move

创建 b 列表时,a 被移动进了 b 这样 b 就拥有了 a。接着当再次尝试使用 a 创建 c 时,发生报错,因为 a 的所有权已经被移动。

修改List枚举的成员Cons为一个值和Rc的元组。相较于Box,使用Rc创建b时,b并不会获取a的所有权,这里通过Rc::clone 克隆Rc,同时增加Rc的引用计数,创建c时同理,因此实现b和c对a所有权的共享。

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 = Rc::new(Cons(3,Rc::clone(&a)));
    let b = Rc::new(Cons(3,Rc::clone(&b)));
}

3. Rc的引用计数

在Rc的引用计数为0之前,Rc指向的数据不会被清理。通过控制台输出Rc::strong_count观察Rc的引用计数变化:

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)))));
    println!("count after creating a = {}", Rc::strong_count(&a));//1
    let b = Cons(3, Rc::clone(&a));
    println!("count after creating b = {}", Rc::strong_count(&a));//2
    {
        let c = Cons(4, Rc::clone(&a));
        println!("count after creating c = {}", Rc::strong_count(&a));//3
    }
    println!("count after c goes out of scope = {}", Rc::strong_count(&a));//2
}

在c作用域内Rc的强引用计数会加1,当离开作用域,c被drop,Rc的引用计数也减1,重新变为2.

tips

  1. Rc::clone会增加Rc的引用计数
  2. 当变量离开作用域时,由于变量被drop,Rc引用计数会减少
  3. Rc不允许多个可变引用,因为其会破环借用规则,相同位置的多个可变借用可能造成数据竞争和不一致。

当前页面是本站的「Baidu MIP」版。查看和发表评论请点击:完整版 »