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