Rust 智能指针(Rc)
2018年08月23日13:16:27 弱冠(kwebi) 阅读数265
std::rc::Rc
Rc
代表引用计数
以下是标准库文档的介绍
Single-threaded reference-counting pointers. ‘Rc’stands for ‘Reference Counted’.
The type Rc provides shared ownership of a value of type T, allocated in the heap. Invoking clone on Rc produces a new pointer to the same value in the heap.
When the last Rc pointer to a given value is destroyed, the pointed-to value is also destroyed.
Shared references in Rust disallow mutation by default, and Rc is no exception: you cannot generally obtain a mutable reference to something inside an Rc. If you need mutability, put a Cell or RefCell inside the Rc; see an example of mutability inside an Rc.
Rc uses non-atomic reference counting. This means that overhead is very low, but an Rc cannot be sent between threads, and consequently Rc does not implement Send. As a result, the Rust compiler will check at compile time that you are not sending Rcs between threads. If you need multi-threaded, atomic reference counting, use sync::Arc.
总结为以下几点:
+ Rc
让一个值有多个所有者,调用clone
产生一个指针指向该值
+ 当Rc
指针全部销毁时,该值也销毁
+ 不能通过Rc
获得可变引用
+ Rc
是非原子引用,只能用于单线程,多线程用Arc
use std::rc::Rc;
fn main() {
let five = Rc::new(5);
let five1 = five.clone();
//Rc实现了Deref trait,可以自动解引用,因此下面打印成功
println!("{}", five1);
//可以通过调用strong_count查看引用计数
println!("{}", Rc::strong_count(&five1));
}
顺便介绍一下rc::Weak
标准库介绍
Weak is a version of Rc that holds a non-owning reference to the managed value. The value is accessed by calling upgrade on the Weak pointer, which returns an Option.
Since a Weak reference does not count towards ownership, it will not prevent the inner value from being dropped, and Weak itself makes no guarantees about the
value still being present and may return None when upgraded.
A Weak pointer is useful for keeping a temporary reference to the value within Rc without extending its lifetime. It is also used to prevent circular references between Rc pointers, since mutual owning references would never allow either Rc to be dropped. For example, a tree could have strong Rc pointers from parent nodes to children, and Weak pointers from children back to their parents.
The typical way to obtain a Weak pointer is to call Rc::downgrade.
Weak
用于不拥有所有权的引用,通过调用upgrade
调用值,这个方法返回一个Optical<Rc<T>>
Weak
不能阻止值被丢弃,当值被丢弃时,调用upgrade
时返回None
- 使用
Weak
可以避免Rc
的循环引用 - 调用
Rc::downgrade
来获得一个Weak
指针
#![allow(unused)]
use std::rc::Rc;
fn main() {
let five = Rc::new(5);
let weak_five = Rc::downgrade(&five);
let strong_five: Option<Rc<_>> = weak_five.upgrade();
println!("{}", strong_five.unwrap());
println!("weak: {}, strong: {}", Rc::weak_count(&five), Rc::strong_count(&five));
}
Rust智能指针——Box
来源: www.dyike.com
本文转载自:https://www.dyike.com/2018/09/24/rust-box-tree/,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有。
在C++11中也有智能指针shared_ptr,unique_ptr,shared_ptr,在Rust中也有叫智能指针的东西,今天我们来讨论一下Box。现在我们要构建一个二叉树。在 Python 中实现是比较简单的:
classTree:
def__init__(self,root_val,left =None,right =None):
self.root =root_val
self.left =left
self.right =right
t =Tree(12,
Tree(10,None,Tree(14)),
Tree(16,Tree(15),Tree(22))))
最终的树形结构:
12
/
10 16
/
14 15 22
现在用Rust来实现一个?
struct Tree {
root: i64,
left: Tree,
right: Tree,
}
在上一篇我们已经提到了,上面的代码Rust肯定不会让我们编译通过。已经提示我们使用 & 、 Box 、 Rc 。他们都是指针类型,都是指向内存上某一个位置。
- & 在Rust里面称作borrow类型。只是引用内存上某个位置的值,并不拥有所指的值。如果我们想使用 & 来修复这个问题,我们需要注意一个问题就是生命周期,borrow类型生命周期取决owner的生命周期。
structTree<'a> {
root: i64,
left: &'a Tree<'a>,
right: &'a Tree<'a>,
}
- Box 是一种智能指针,零运行时开销。拥有它所指向的数据。我们为什么称做智能的,是因为当执行过边界,它将drop掉它所指向的数据然后drop本身。不需要手动管理内存!!!
structTree{
root:i64,
left:Box<Tree>,
right:Box<Tree>,
}
- Rc 是另外一种智能指针,是 reference count 的简称。用于记录数据结构被引用的次数。当引用的数字减小到0时,就自行清理。如果在一个线程,对于同一个数据有多个owner的时候,我们选用 Rc 。对于多线程,我们有 Arc (atomic reference count)
structTree{
root:i64,
left:Rc<Tree>,
right:Rc<Tree>,
}
上面的三个方法都可以解决之前的问题,那我们该怎么选择呢,这取决于自己的使用场景。
Make subtrees optional
紧接着我们又面临的问题就是无法实例化之前定义的tree结构。为什么呢?现有的数据结构,left和right子树都是Box 的类型,但是我们定义的tree节点有一些是空子树。在Python代码中,我们使用了 None ,但在Rust中有 Option :
structTree{
root:i64,
left:Option<Box<Tree>>,
right:Option<Box<Tree>>,
}
现在我们就可以创建我们第一个tree:
Tree{
root:12,
left:Some(Box::new(Tree{
root:10,
left:None,
right:Some(Box::new(Tree{
root:14,
left:None,
right:None,
})),
})),
right:Some(Box::new(Tree{
root:16,
left:Some(Box::new(Tree{
root:15,
left:None,
right:None,
})),
right:Some(Box::new(Tree{
root:22,
left:None,
right:None,
})),
})),
};
现在将代码做一些优化:
#[derive(Default)]
structTree{
root:i64,
left:Option<Box<Tree>>,
right:Option<Box<Tree>>,
}
impl Tree{
fn new(root:i64)->Tree{
Tree{
root:root,
..Default::default()
}
}
fn left(mut self,leaf:Tree)->Self{
self.left =Some(Box::new(leaf));
self
}
fn right(mut self,leaf:Tree)->Self{
self.right =Some(Box::new(leaf));
self
}
}
Tree::new(12)
.left(
Tree::new(10)
.right(Tree::new(14))
)
.right(
Tree::new(16)
.left(Tree::new(15))
.right(Tree::new(22))
);
那为什么在Python就能完美运行呢?Python在运行的时候为树对象动态分配内存,将所有内容包装在PyObject中,类似Rc。
遇到类似的情况我们该怎么处理和选择呢?我们需要了解所有可能的解决方案,如果可以,我们就远离使用智能指针,坚持简单借用。如果不能,我们就选择侵入性更小的一个。