我目前正在为Sway WM做一个小的状态栏.条形图由多个实现Widget trait的对象组成. 在所有小部件中,时钟有点特殊.我不只是想渲染它,而且我还想确保它的渲染是及时完成的,否则显示的时间往往会有点偏离.由于这一要求,我需要既与时钟作为一个Widget,也作为自己的Clock.

我面临的问题是,一旦时钟小部件与其他小部件一起存储在一个向量中,borrow 判断器就不允许该向量之外的任何可变引用存在.这使得不可能在主应用程序循环中将时钟用作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()
        });
    }
}

我知道不同的体系 struct 可以解决这个问题,但我只是有兴趣了解这个问题的一些解决方案,以便下次看到类似的情况时知道该怎么做.

推荐答案

铁 rust 不允许uncontrolled个共享的、可变的访问.对于您的应用程序,您需要共享、可变的时钟访问权限,因此您必须 Select 一种方式来控制它.有许多可能性,但后果各不相同.

  1. 您可以将每个小部件放在RefCellMutexRwLock中,以允许运行时判断的Mutations .

    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. 你可以记下时钟在向量中的位置,然后将其作为widgets[index_of_the_clock].get_alignment_delay()访问.如果用户可以添加和删除多个动画小部件,而您想要判断所有人的睡眠时间,这可能是有意义的.

  3. 您可以要求每个小部件都是内部可变的(也就是说,将RefCell之类的东西用于其所有可变部分),这样update()只需要&self而不是&mut self.然后,您可以与&共享对小部件的访问,如上面的代码示例所示,但不需要包装RefCell.(这可能比第一种 Select 更不符合人体工程学.)

请注意,在所有这些情况下,您可能会发现使用引用向量是有局限性的--特别是,如果您希望让用户在程序运行时添加新的小部件,那么向量应该真正地own小部件而不是borrow 它们.这意味着向量的类型将是Vec<Box<dyn Widget>>(或在情况1中,Vec<Box<RefCell<dyn Widget>>>).这并不会阻止您改变小部件,因为ownership包含了改变的能力.

Rust相关问答推荐

如何在 struct 中填充缓冲区并同时显示它?

什么是谓词的简短和简洁类型

基于对vec值的引用从该值中删除该值

使用pyo3::Types::PyIterator的无限内存使用量

铁 rust 中的共享对象实现特征

什么时候和为什么S最好是按值或引用传递简单类型

找不到 .has_func 或 .get_func 的 def

try 实现线程安全的缓存

write_buffer 不写入缓冲区而是输出零 WGPU

通过写入 std::io::stdout() 输出不可见

仅在使用 &mut 或线程时borrow 的数据在闭包之外转义?

实现AsyncWrite到hyper Sender时发生生命周期错误

decltype、dyn、impl traits,重构时如何声明函数的返回类型

使用在功能标志后面导入的类型,即使未启用功能标志

简单 TCP 服务器的连接由对等重置错误,mio 负载较小

如何使用泛型满足 tokio 异步任务中的生命周期界限

以 `static` 为前缀的闭包是什么意思?我什么时候使用它?

Rust 将特性传递给依赖项

如何在 Rust 的泛型函​​数中同时使用非拥有迭代器和消费迭代器?

来自外部函数的future 内部可变引用