• Rust的智能指针--RefCell<T>


    Rust的智能指针–RefCell


    现在我定义一个接口Messager,用来send消息。

    pub trait Messager {
        fn send(&self, msg: &str);
    }
    
    • 1
    • 2
    • 3


    然后一个LimitTracker类型根据指定的配额来send消息*

    pub struct LimitTracker<'a, T: Messager> {
        messager: &'a T,
        value: usize,
        max: usize,
    }
    impl<'a, T> LimitTracker<'a, T>
    where
        T: Messager,
    {
        pub fn new(messager: &'a T, max: usize) -> LimitTracker<'a, T> {
            LimitTracker { messager: messager, value: 0, max: 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.messager.send("Error, You are over your quota!");
            } else if percentage_of_max >= 0.9 {
                self.messager.send("Urgent warning: You have used up over 90% of your quota!");
            } else if percentage_of_max >= 0.75 {
                self.messager.send("Warning: You have used up over 75% of your quota!");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24


    最后是一段测试代码,用来测试set_value的返回是否符合预期*

    #[cfg(test)]
    mod tests {
        use super::*;
    
        struct MockMessager {
            send_messages: Vec<String>,
        }
        impl MockMessager {
            fn new() -> MockMessager {
                MockMessager { send_messages: vec![] }
            }
        }
        impl Messager for MockMessager {
            fn send(&self, msg: &str) {
                self.send_messages.push(String::from(msg));
            }
        }
    
        #[test]
        fn send_an_over_75_percentage_message() {
            let mock_messager = MockMessager::new();
            let mut limit_tracker = LimitTracker::new(&mock_messager, 100);
            limit_tracker.set_value(80);
            assert_eq!(mock_messager.send_messages.len(), 1);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27


    我们发现上面的self.send_messages.push(String::from(msg));这一段报错了。很简单:因为我们最开始定义的send方法是一个immutable borrow reference,即:&self。所以不可以修改MockMessager实例的值。

    解决方法有两种:第一种是直接修改Messager trait,send方法接收&mut self;第二种是使用Interior Mutable Pattern(A Mutable Borrow to an Immutable Value),可以使用RefCell智能指针,获得一个可变借用。

    下面是第二种方法的全部代码:

    // implement a mock object
    pub trait Messager {
        fn send(&self, msg: &str);
    }
    pub struct LimitTracker<'a, T: Messager> {
        messager: &'a T,
        value: usize,
        max: usize,
    }
    impl<'a, T> LimitTracker<'a, T>
    where
        T: Messager,
    {
        pub fn new(messager: &'a T, max: usize) -> LimitTracker<'a, T> {
            LimitTracker { messager: messager, value: 0, max: 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.messager.send("Error, You are over your quota!");
            } else if percentage_of_max >= 0.9 {
                self.messager.send("Urgent warning: You have used up over 90% of your quota!");
            } else if percentage_of_max >= 0.75 {
                self.messager.send("Warning: You have used up over 75% of your quota!");
            }
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        use std::cell::RefCell;
    
        struct MockMessager {
            send_messages: RefCell<Vec<String>>,
        }
        impl MockMessager {
            fn new() -> MockMessager {
                MockMessager { send_messages: RefCell::new(vec![]) }
            }
        }
        impl Messager for MockMessager {
            fn send(&self, msg: &str) {
                self.send_messages.borrow_mut().push(String::from(msg));
            }
        }
    
        #[test]
        fn send_an_over_75_percentage_message() {
            let mock_messager = MockMessager::new();
            let mut limit_tracker = LimitTracker::new(&mock_messager, 100);
            limit_tracker.set_value(80);
            assert_eq!(mock_messager.send_messages.borrow().len(), 1);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    我使用RefCell将MockMessager中需要修改的值包装起来,然后通过borrow_mut()获得它的mutable 引用,通过borrow()获得immutable引用,从而可以达到修改MockMessager的目的。

    注意:

    RefCell和通常的Rust规则很不同,他是在runtime时进行borrow check,所以如果在程序运行时违反了Rust的借用规则,那么程序会Panic或者Exit。而不是想通常情况下直接Compile Error。另外,这种模式会有一些性能开销,因为需要在运行时跟踪它是否符合借用规则。

  • 相关阅读:
    代码随想录三刷day37
    【JavaEE】Spring的创建和使用(保姆级手把手图解)
    shell脚本中实现远程和其他用户的子shell执行
    Sentinel入门开发
    web分析-手把手教你怎么写小程序事件分析
    Arduino开发实例-DIY风速测量及显示
    Nginx | nginx配置https
    剑指 Offer 34. 二叉树中和为某一值的路径
    行业方案|“机场”行业智能运维解决方案介绍
    dbeaver导入excel数据
  • 原文地址:https://blog.csdn.net/qq_46480020/article/details/126199203