写点什么

5 分钟速读之 Rust 权威指南(二十七)Rc<T>

用户头像
码生笔谈
关注
发布于: 刚刚
5分钟速读之Rust权威指南(二十七)Rc<T>

使用 Rc<T>共享数据

前面我们有使用 Box<T>实现链表,本节我们以一道题开始,先试着实现图中的列表:


[配图]


图中列表 a 包含 5 ,后面跟着的是 10,列表 b 从 3 开始,列表 c 从 4 开始。b 和 c 会接上包含 5 和 10 的列表 a。换句话说,这两个列表会尝试共享 a 列表所包含的 5 和 10。

使用 Box<T>实现

我们先尝试使用已经学过的 Box<T>类型来实现:


enum List {  Cons(i32, Box<List>),  Nil,}
// 创建a列表let a = List::Cons(5, Box::new(List::Cons(10, Box::new(List::Nil)) ));
// 创建b列表,并连接a列表let b = List::Cons(3, Box::new(a));
// 创建c列表,并连接a列表let c = List::Cons(4, Box::new(a)); // 报错,a的所有权已经被移动
复制代码

利用引用来解决 a 的所有权转移的问题

我们可以将 Box 中的列表类型改为引用类型,这样 b 和 c 列表就可以同时引用 a 列表了:


#[derive(Debug)]enum List<'a> {  Cons(i32, Box<&'a List<'a>>),  Nil,}
复制代码


引用方式一:


let a = List::Cons(5,  Box::new(&List::Cons(10,    Box::new(&List::Nil)) // 报错,创建临时引用&Nil,但是在a赋值时已经被销毁  )); // &Nil在这里的结束语句被销毁,此时a还没创建完成println!("{:?}", a);
复制代码


引用方式二:


let a2 = &List::Nil;let a1 = &List::Cons(10, Box::new(a2));let a = &List::Cons(5, Box::new(a1));
let b = List::Cons(3, Box::new(a));let c = List::Cons(4, Box::new(a));println!("{:?}", b); // Cons(3, Cons(5, Cons(10, Nil)))println!("{:?}", c); // Cons(4, Cons(5, Cons(10, Nil)))
复制代码


虽然可以实现,但是嵌套关系不是很直观。

使用 Rc<T>来解决

Rc<T>支持多重所有权,它名称中的 Rc 是 Referencecounting(引用计数)的缩写。对于了解垃圾回收概念的同学理解起来会更顺畅一些,Rc<T>类型会在内部维护一个引用次数计数器,用于确认这个值是否仍在使用。如果对一个值的引用次数为零,那么就意味着这个值可以被安全地清理掉,而不会触发引用失效的问题:


use std::rc::Rc; // 引入Rc#[derive(Debug)]enum List {  Cons(i32, Rc<List>), // 替换Box为Rc  Nil}
// 创建Rc实例let a = Rc::new(List::Cons(5, Rc::new(List::Cons(10, Rc::new(List::Nil) ))));
// 这里的Rc::clone只是增加的引用计数,虽然使用a.clone也可以实现,但是数据会被深拷贝let b = List::Cons(3, Rc::clone(&a));
// 再次增加引用次数let c = List::Cons(4, Rc::clone(&a));
println!("{:?}", b); // Cons(3, Cons(5, Cons(10, Nil)))println!("{:?}", c); // Cons(4, Cons(5, Cons(10, Nil)))
复制代码

观察引用计数

通过 Rc<T>提供的方法可以获取到当前的引用数量:


let a = Rc::new(List::Cons(5,  Rc::new(List::Cons(10,    Rc::new(List::Nil)  ))));
println!("创建a之后的引用计数:{}", Rc::strong_count(&a));// 创建a之后的引用计数:1
let b = List::Cons(3, Rc::clone(&a));println!("创建b之后的引用计数:{}", Rc::strong_count(&a));// 创建b之后的引用计数:2
// 进入新的作用域{ let c = List::Cons(2, Rc::clone(&a)); println!("创建c之后的引用计数:{}", Rc::strong_count(&a)); // 创建c之后的引用计数:3} // c离开作用域时自动将引用计数减1。
println!("销毁c之后的引用计数:{}", Rc::strong_count(&a));// 销毁c之后的引用计数:2
复制代码


Rc<T>通过不可变引用使得程序的不同部分之间可以共享只读数据。如果 Rc<T>也允许持有多个可变引用的话,那么它就会违反借用规则:多个指向同一区域的可变借用会导致数据竞争及数据不一致。

发布于: 刚刚阅读数: 2
用户头像

码生笔谈

关注

欢迎关注「码生笔谈」公众号 2018.09.09 加入

前端、Rust干货分享,一起成为更好的Javascripter & Rustacean

评论

发布
暂无评论
5分钟速读之Rust权威指南(二十七)Rc<T>