Create chapter_3_16_5.md

This commit is contained in:
令狐一冲
2023-05-18 09:59:49 +08:00
committed by GitHub
parent 35c0825c88
commit 6654760347

View File

@@ -0,0 +1,85 @@
# 3.16.5 Rc智能指针
## 1. 使用场景分析
假定有这样一个需求,希望创建两个共享第三个列表所有权的列表,其概念类似于如下图:
![注释](../../assets/26.png)
根据前面的知识,可能写出来的代码如下:
```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))));
let b = Cons(3, Box::new(a));
let c = Cons(4, Box::new(a));
}
```
但是上面的代码报错,因为**Rust 所有权机制要求一个值只能有一个所有者**。为了解决此类需求Rust提供了Rc智能指针。
## 2. 使用Rc共享数据
Rc智能指针通过引用计数解决数据共享的问题下面是Rc使用的简单代码
```Rust
use std::rc::Rc;
fn main() {
let a = Rc::new(5u32);
let b = Rc::clone(&a);
let c = a.clone();
}
```
上面代码中a、b、c就共享数据5。当创建b时不会获取a的所有权会克隆a所包含的Rc(5u32)这会使引用计数从1增加到2并允许a和b共享Rc(5u32)的所有权。
创建c时也会克隆 a引用计数从2增加为3。每次调用Rc::cloneRc(5u32)的引用计数都会增加,直到有零个引用之前其数据都不会被清理。其在内存中的表示如下:
![注释](../../assets/27.png)
前面共享列表的需求则可以使用Rc实现如下
```Rust
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
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(4, a.clone()); //此处使用a.clone()也是ok的
}
```
对应的内存示意图如下:
![注释](../../assets/28.png)
## 3. 打印Rc的引用计数
下面的示例打印了Rc的引用计数
```Rust
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
let _b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let _c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
```
使用Rc需要注意以下两点
- 通过Rc是允许程序的多个部分之间只读的共享数据因为相同位置的多个可变引用可能会造成数据竞争和不一致。如果涉及到修改需要使用RefCell或者Mutex。
- Rc只能是同一个线程内部共享数据它是非线程安全的。如果要在多线程中共享需要使用Arc。