使用 Rust 惯用的方法来获得具有单个向量项别名的特征向量是什么?

问题描述 投票:0回答:1

我目前正在为 Sway WM 开发一个小型状态栏。该栏由多个实现

Widget
特征的对象组成。 在所有小部件中,时钟有点特别。我不仅想渲染它,还想确保它的渲染及时完成,否则显示的时间通常会有点偏差。由于这个要求,我需要将时钟作为
Widget
和它自己的
Clock
来使用。

我面临的问题是,一旦时钟小部件与其他小部件一起存储在向量中,借用检查器就不允许向量外部存在任何可变引用。这使得无法在主应用程序循环中使用时钟作为

Clock
的实例。

我发现解决这个问题的最干净的方法是通过原始指针和不安全的代码,而不引入运行时开销。我想知道这个问题的其他零运行时开销解决方案。

这是代码的过度简化版本,说明了当前代码的样子:

trait Widget {
    fn update_info(&mut self);
    fn render(&self);
}

struct NetworkStatus{}
impl Widget for NetworkStatus {
    fn update_info(&mut self) {
        // query the OS for network information
    }
    fn render(&self) {
        println!("Connected wirelessly");
    }
}

struct Clock{}
impl Widget for Clock {
    fn update_info(&mut self) {
        // query the OS for the current time
    }
    fn render(&self) {
        println!("2024-03-06T19:25:17");
    }
}
impl Clock {
    fn get_alignment_delay(&mut self) -> std::time::Duration {
        return std::time::Duration::new(1, 0);
    }
}

fn main() {
    let mut netstat = NetworkStatus{};
    let mut clock = Clock{};
    let clock_ptr = &mut clock as *mut Clock;

    let mut widgets: Vec<&mut dyn Widget> = vec![
        &mut netstat,
        &mut clock,
    ];

    loop {
        for w in widgets.iter_mut() {
            w.update_info();
        }

        for w in widgets.iter() {
            w.render();
        }

        std::thread::sleep(unsafe {
            (*clock_ptr).get_alignment_delay()
        });
    }
}

我知道不同的架构可以解决这个问题,但我只是有兴趣学习一些针对这个问题的解决方案,这样我下次看到类似的问题时就知道该怎么做。

rust traits borrow-checker unsafe
1个回答
0
投票

Rust 不允许“不受控制”、共享、可变访问。对于您的应用程序,您需要对时钟进行共享、可变的访问,因此您必须选择一种方法来控制它。有很多种可能性,会带来不同的后果。

    您可以将每个小部件放入
  1. RefCell

    Mutex
    RwLock
    中以允许运行时检查突变。
    fn main() {
        let mut netstat = RefCell::new(NetworkStatus{});
        let mut clock = RefCell::new(Clock{});
    
        // note these are now `&` references, which may be shared
        let mut widgets: Vec<&RefCell<dyn Widget>> = vec![
            &netstat,
            &clock,
        ];
    
        loop {
            for w in widgets.iter() {
                w.borrow_mut().update_info();
            }
    
            for w in widgets.iter() {
                w.borrow().render();
            }
    
            std::thread::sleep(clock.borrow().get_alignment_delay());
        }
    }
    

  2. 您可以记下时钟在矢量中的位置,然后以
  3. widgets[index_of_the_clock].get_alignment_delay()

    的方式访问它。如果用户可以添加和删除多个动画小部件,并且您想检查所有这些小部件的睡眠时间,这可能是有意义的。

    
    

  4. 您可以要求每个小部件都是内部可变的(也就是说,对自身的所有可变部分使用
  5. RefCell

    之类的东西),这样

    update()
    只需要
    &self
    而不是
    &mut self
    。然后,您可以像上面的代码示例一样与
    &
    共享对小部件的访问,但不需要包装
    RefCell
    。 (这可能比第一个选项不太符合人体工程学。)
    
    

  6. 请注意,在所有这些情况下,您可能会发现使用引用向量是有限制的——特别是,如果您想让用户在程序运行时添加新的小部件,则向量应该真正
拥有

小部件而不是借用它们。这意味着向量的类型为 Vec<Box<dyn Widget>>(或者在情况 1 中为

Vec<Box<RefCell<dyn Widget>>>
)。这不会阻止您改变小部件,因为
所有权
包括改变的能力。

© www.soinside.com 2019 - 2024. All rights reserved.