[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,所以内存也就永远不会释放了 );

 

posted @ 2023-04-04 20:19  朝花不夕拾  阅读(76)  评论(0编辑  收藏  举报