咋一看覺得這兩個概念差的很遠,其實他們之間還是有聯系的:
-
相同點:
他們都是在堆上配置設定資源,都隻是儲存了一個指向堆上的指針
- 差別:
- 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());
println!("r1:{:p}",&*r1);
//clone計數
r = r1.clone();
//多處引用,都可以分别通路
println!("r1:{:?}",r1);
let b1 = Box::new(Foo());
println!("b1:{:p},data:{}",&*b1,(*b1).);
//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還是繼續使用的