文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
7.7.2 智能
智能指针(smart pointer)是一类数据结构,其行为类似指针,但拥有额外功能。与引用的差别在于,引用 借用 (borrow)目标,而智能指针则 拥有 (own)目标。
智能指针通常实现 Deref
和 Drop
特征。
Deref
: 重载解引用运算符,使其操作与引用一致。Drop
: 负责在离开作用域时清理资源。
类型:
Box<T>
: 在堆上分配值。Rc<T>
: 引用计数,可以有多个所有者。RefCell<T>
: 以不可变引用修改值。
Box
在堆(heap)上为值分配内存,栈上只是一个指向该内存的指针。
- 单一所有者。
- 运行时才知所需内存大小。
- 转移所有权时,不想复制数据。
stack heap +=====+ +=======+ | ptr | --------> | value | Box<T> +=====+ +=======+
使用方式和引用一致,没什么特殊功能。超出作用域自动释放。
fn main() { // 在堆上分配 sizeof(i32) 大小内存,并存储值 5。 let mut p = Box::new(5); *p += 1; let x: i32 = *p; assert_eq!(x, 6); }
fn main() { // 以数组实现类 calloc 功能。 let mut p = Box::new([0i64; 10]); p[1] = 10; p[2] = 20; println!("{:?} {:?}", p[2], p); }
Rc
引用计数(reference counting)启用多所有权。当引用数归零时,进行清理。
- 堆分配内存,共享所有权。
- 单线程,使用非原子计数。
- 不可变引用,无法修改值。
- 降级获取没有所有权的弱指针(weak pointer)。
- 存在循环引用无法释放的问题(用弱指针解决)。
stack heap +=====+ +============+ | ptr | --------> | strong_cnt | Rc<T> +=====+ +------------+ | weak_cnt | +------------+ | value | +============+
use std::rc::Rc; fn main() { let rc = Rc::new(5); assert_eq!(Rc::strong_count(&rc), 1); { // 增加引用,计数 +1。 let rc2 = rc.clone(); assert_eq!(Rc::strong_count(&rc), 2); // 解引用。 assert_eq!(*rc2, 5); } // 超出作用域,计数 -1。 assert_eq!(Rc::strong_count(&rc), 1); }
use std::rc::{Rc, Weak}; fn main() { let wr: Weak<i32>; { let rc = Rc::new(5); // 降级,生成弱引用。 wr = Rc::downgrade(&rc); // 不影响强引用计数。 assert_eq!(Rc::strong_count(&rc), 1); assert_eq!(Rc::weak_count(&rc), 1); // 弱引用不能保证目标值存活,所以不能直接解引用。 // 升级成强引用再操作。如已释放,返回 None。 if let Some(rc2) = wr.upgrade() { assert_eq!(*rc2, 5); } else { panic!("upgrade: None"); } } // Rc drop!!! assert_eq!(wr.upgrade(), None); }
自动解引用,可直接调用值方法。
use std::rc::Rc; struct User { age: u8 } impl User { fn test(&self) { println!("{}", self.age); } } fn main() { let rc = Rc::new(User{ age: 10 }); rc.test(); }
另有原子版本的 Arc,参考后续《并发:同步》章节。
RefCell
内部可变性 (Interior mutability)绕过借用规则,允许通过不可变引用修改内部值。
- 单一所有权,运行期借用规则检查。
- 运行期行为,出错
panic!
。 - 单线程。
编译器静态规则检查过于保守,某些设计需要运行期操作。
+============+ | borrow_cnt | RefCell<T> +------------+ borrow_mut: count = -1 | value | +============+
获取可变和不可变引用。
方法
borrow
、borrow_mut
返回Ref<T>
、RefMut<T>
。
它们实现了Deref
,但RefCell
没有。
use std::cell::RefCell; fn main() { let c = RefCell::new(5); { let r1 = c.borrow(); // &T let r2 = c.borrow(); // &T assert_eq!(*r1, 5); assert_eq!(*r1, *r2); } { let mut r = c.borrow_mut(); // &mut T *r = 10; } assert_eq!(c.into_inner(), 10); }
fn main() { let c = RefCell::new(5); let mut r1 = c.borrow_mut(); // &mut T // 同一时刻,不能有多个可变引用。 let mut r2 = c.borrow_mut(); // &mut T ~~~~~~~~~~ already borrowed: BorrowMutError *r1 += 1; *r2 += 1; }
改造 Rc
,使其可变。
use std::rc::Rc; use std::cell::RefCell; fn main() { let rc = Rc::new(RefCell::new(5)); let rc2 = rc.clone(); { let mut r = rc.borrow_mut(); *r = 100; } assert_eq!(*rc2.borrow(), 100); }
Cell
便捷版本,开辟一块始终可变内存区域。
RefCell
提供引用,Cell
提供值。RefCell
动态借用检查规则,Cell
是可变内存位置。
use std::cell::Cell; struct Data { x: i64, y: Cell<i64>, } fn main() { let d = Data{ x: 1, y: Cell::new(2) }; // d.x = 10; // ^^^^^^^^ cannot assign d.y.set(200); assert_eq!(d.y.get(), 200); // copy assert_eq!(d.y.get(), 200); // copy assert_eq!(d.y.take(), 200); // move assert_eq!(d.y.take(), 0); }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论