[rust学习] 二、 rust中的智能指针
rust中智能指针大致分类
以下内容提炼自rust官方文档: https://doc.rust-lang.org/book/ch15-01-box.html
一、 Box<T>
1. 使用例子:
1 fn main() { 2 let b = Box::new(5); 3 println!("b = {}", b); 4 }
2. 特性:
a. 由Box指向的数据的内存分配是在堆上;
b. 在编译期执行的可变借用与不可变借用的检查;
c. 所有权拥有者只有1个;
3. 大部分的使用场景:
a. 编译时某类型大小不确定,但希望在大小确定的上下文中使用该类型的值;
b. 有大量数据需要进行所有权的转移,但不希望进行copy操作;
c. 想拥有一个实现特定trait而不是某个确定类型的值 ;
二. Rc
1. 使用例子:
enum List { Cons(i32, Rc<List>), Nil, } use crate::List::{Cons, Nil}; use std::rc::Rc; fn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); let b = Cons(3, Rc::clone(&a)); let c = Cons(4, Rc::clone(&a)); }
2. 特性:
a. Rc指向的数据可以有多个拥有者(可变借用只能有一个),这也是这个Rc指针会存在的初衷;
b. 每进行一次clone,其指向数据的强引用的计数次数会+1,只有当强引用次数为0时该指向数据所占内存才会被释放;有强引用这个概念,对应的就有一个弱引用的概念存在。弱引用是用来在一些循环引用的数据结构中使用的,用来防止发生环形引用从而产生的内存泄漏;
c. Rc不是线程安全的,不能在多线程中使用;
d. 在编译期执行的可变借用与不可变借用的检查;
3. 使用场景:
a. 共享一份数据到多个需要所有权的地方使用;
三. RefCell
1. 使用例子:
pub trait Messenger { fn send(&self, msg: &str); } pub struct LimitTracker<'a, T: Messenger> { messenger: &'a T, value: usize, max: usize, } impl<'a, T> LimitTracker<'a, T> where T: Messenger, { pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> { LimitTracker { messenger, value: 0, max, } } pub fn set_value(&mut self, value: usize) { self.value = value; let percentage_of_max = self.value as f64 / self.max as f64; if percentage_of_max >= 1.0 { self.messenger.send("Error: You are over your quota!"); } else if percentage_of_max >= 0.9 { self.messenger .send("Urgent warning: You've used up over 90% of your quota!"); } else if percentage_of_max >= 0.75 { self.messenger .send("Warning: You've used up over 75% of your quota!"); } } }
1 #[cfg(test)] 2 mod tests { 3 use super::*; 4 use std::cell::RefCell; 5 6 struct MockMessenger { 7 sent_messages: RefCell<Vec<String>>, 8 } 9 10 impl MockMessenger { 11 fn new() -> MockMessenger { 12 MockMessenger { 13 sent_messages: RefCell::new(vec![]), 14 } 15 } 16 } 17 18 impl Messenger for MockMessenger { 19 fn send(&self, message: &str) { 20 self.sent_messages.borrow_mut().push(String::from(message)); 21 } 22 } 23 24 #[test] 25 fn it_sends_an_over_75_percent_warning_message() { 26 // --snip-- 27 28 assert_eq!(mock_messenger.sent_messages.borrow().len(), 1); 29 } 30 }
2. 特性:
a. 内部可变性。类似上面的代码例子,虽然send函数的入参是不可变的&self,由于使用了RefCell,依旧可以对sent_messages属性进行修改;
b. 类似Box,只有一个拥有者;
c. 非线程安全,不可在多线程中使用;
d. 运行时的借用检查,而非编译时;
3. 使用场景:
a. 类似示例代码一般,外部不可变,而内部属性想进行改变;
b. 部分代码逻辑复杂,rust编译器在编译期完全分析出是否有内存安全是很困难的,可能产生误判断导致代码无法编译通过。在人工确保正确性的情况下,是用RefCell可以避免掉编译期的借用检查。
4. 注意事项:
a. 用 RefCell<Rc<T>> 这种结构可能导致环形引用,从而产生内存泄漏 ( 具体原因请看官方文档章节:https://doc.rust-lang.org/book/ch15-06-reference-cycles.html , 简单点说明就是因为RefCell的内部可变性,可能导致Rc指向的数据发生变动,从而与自己本身产生直接活间接的一个环形引用,由于强引用计数永远不为0,所以内存也就永远不会释放了 );