咋一看觉得这两个概念差的很远,其实他们之间还是有联系的:
- 相同点:
他们都是在堆上分配资源,都只是保存了一个指向堆上的指针 - 区别:
- Box就是一个简单的指向堆的指针,并且指向堆的这个区域的指针只能有一个,owner lifetime结束就会释放堆上的资源
- Rc增加了一个计数(准确来说是两个指针),可以多个指针指向堆的这个区域,等所有的引用者lifetime结束了才释放堆上的资源
下面的代码为了看起来清爽点删除了注释
1 这个是Rc的定义
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
pub struct Rc<T: ?Sized> {
_ptr: NonZero<*mut RcBox<T>>,
}
其中NonZero很简单,就是简单的包装了下T
pub struct NonZero<T: Zeroable>(T);
具体可以看:
http://doc.rust-lang.org/stable/src/alloc/rc.rs.html#172
2 Box定义如下:
pub struct Box<T>(Unique<T>);
http://doc.rust-lang.org/stable/src/alloc/boxed.rs.html#94
其中Unique定义如下:
pub struct Unique<T: ?Sized> {
pointer: NonZero<*const T>,
_marker: PhantomData<T>,
}
http://doc.rust-lang.org/stable/src/core/ptr.rs.html#513-521
Unique里的marker是不占用存储空间的,可以暂时忽略掉
3 具体的例子:
use std::rc::Rc;
#[derive(Debug)]
struct Foo(i32);
fn main(){
let r:Rc<Foo>;
let b:Box<Foo>;
{
let r1 = Rc::new(Foo(100));
println!("r1:{:p}",&*r1);
//clone计数
r = r1.clone();
//多处引用,都可以分别访问
println!("r1:{:?}",r1);
let b1 = Box::new(Foo(200));
println!("b1:{:p},data:{}",&*b1,(*b1).0);
//owner ship 转移了
b = b1;
//b1不能再访问
//println!("{:?}",b1);
}
println!("{:?}",r);
println!("r:{:p}",&*r);
println!("{:?}",b);
println!("b:{:p}",&*b);
}
Play地址:http://is.gd/rGBLaW
输出结果:
r1:0x7f621582d010
r1:Foo(100)
b1:0x7f6215823020,data:200
Foo(100)
r:0x7f621582d010
Foo(200)
b:0x7f6215823020
Box,Rc 都是在堆上分配的,那么都可以存在于定义它的范围之外。
- Rc是通过引用计数的方式获得更长的lifetime,这儿r=r1.clone()就把堆上的对象的lifetime增加到了外层block对应的lifetime
- Box通过转移owner ship的方式把b1传递给了b从而让Box指向的堆上的资源没有被释放。
在这种情况下都可以通过不同的方式实现lifetime的扩张,但还是有区别的:
- Box方式转移了owner ship之后就不能再访问原来的b1了,也就是只能一个指针指向Box在堆上分配的资源
- Rc通过增加引用计数的方式实现了lifetime的扩张,并且r1还是继续使用的