【问题标题】:Rust lifetime issue brought on by Mutex of data数据互斥带来的 Rust 生命周期问题
【发布时间】:2021-03-07 00:15:29
【问题描述】:

我已尽可能缩小范围。我有一个对Senders 的引用向量,并希望在receive() 完成运行后处理它们。但是,我遇到了终身管理问题。内部结构是Mutex protected 并包含我试图引用的Senders。我使用 Mutex 来获得数据的可变性,我对替代方案持开放态度。关于如何解决这个问题的任何建议?我不想改变receive()的签名。

use std::sync::Mutex;
use std::sync::mpsc;

#[derive(Default)]
struct SaveForLater<'a> {
    queue: Vec<(&'a mpsc::Sender<usize>, usize)>,
}
impl<'a> SaveForLater<'a> {
    fn send(&mut self, channel: &'a mpsc::Sender<usize>, value: usize) {
        self.queue.push((channel, value));
    }
}

#[derive(Default)]
struct Forwarder {
    data: Mutex<ForwarderData>,
}
#[derive(Default)]
struct ForwarderData {
    senders: Vec<mpsc::Sender<usize>>,
}

impl Forwarder {
    fn with_capacity(capacity: usize) -> Self {
        let mut senders = Vec::new();
        for _ in 0..capacity {
            let (s,r) = mpsc::channel();
            senders.push(s);
        }
        let data = ForwarderData { senders };
        let data = Mutex::new(data);
        Self { data }
    }

    fn receive<'a>(&'a self, value: usize, sender: &mut SaveForLater<'a>) {
        match value {
            0 => { self.data.lock().unwrap().senders.drain(..); },
            _ => {
                let data = self.data.lock().unwrap();
                sender.send(&data.senders[0], value); },
/*
error[E0597]: `data` does not live long enough
  --> src/main.rs:40:30
   |
35 |     fn receive<'a>(&'a self, value: usize, sender: &mut SaveForLater<'a>) {
   |                -- lifetime `'a` defined here
...
40 |                 sender.send(&data.senders[0], value); },
   |                 -------------^^^^-------------------  - `data` dropped here while still borrowed
   |                 |            |
   |                 |            borrowed value does not live long enough
   |                 argument requires that `data` is borrowed for `'a`
*/
        }
    }
}

fn main() {
    let fwd = Forwarder::with_capacity(3);
    {
        let mut sender = SaveForLater::default();
        let value: usize = 42;
        fwd.receive(value, &mut sender);
        for (s,v) in sender.queue {
            s.send(v);
        }   
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_main() {
        main();
    }
}

查看playground中的错误

【问题讨论】:

    标签: rust


    【解决方案1】:

    Mutex 在这里让人分心;真正的问题是试图将引用存储在结构中。让我们看一下mpsc::Sender 的文档,看看它应该如何使用:

    Rust 的异步 channel 类型的发送部分。这一半只能由一个线程拥有,但可以克隆发送给其他线程。

    Sender 甚至没有实现Sync,所以即使你让借用检查器高兴you won't be able to share a single one between multiple threads。它应该被克隆

    struct SaveForLater {
        queue: Vec<(mpsc::Sender<usize>, usize)>,
    }
    
        fn receive(&self, value: usize, sender: &mut SaveForLater) {
            // ...
                    sender.send(data.senders[0].clone(), value);
                    //                         ^^^^^^^^
    

    Here's a full example。不需要生命周期参数。

    一般来说,您应该避免将引用放在结构中,因为只有在编译时非常努力地证明生命周期关系时才能安全地完成。在大多数情况下,您应该简单地克隆数据,或者如果这是错误的语义,请使用ArcRcmpsc::Sender 在内部使用 Arc 以降低克隆成本。

    【讨论】:

    • 我使用 mpsc::Sender 作为简化。实际代码使用的是 smol::channel::Sender,我希望避免 Arc 或 Sender 的 clone() 开销。
    • 借用检查器从根本上进行静态生命周期分析,并且您有生命周期的数据无法静态分析,因为它们依赖于运行时行为.如果您不打算将所有 Senders 存储在竞技场中,或者预填充 Vec 或其他东西(意味着它们都立即被销毁),您需要运行时检查某种(或者你可以只是泄漏内存)。如果您需要,它不是开销。
    • 啊,是的,就是这样——它会进行静态生命周期分析,当然我看着它并说它很好,但这只是因为我在看代码结构和用法,本质上是在做运行时分析。谢谢你让我直截了当。
    • 没错!是的,我认为 Rust 编译器有时似乎足够神奇,当被问到它拒绝执行 literal magic 时,我们会感到失望。无论如何,这种情况发生在我身上很多次。
    • 所以,已经解决了……现在我只需要弄清楚如何从存储在 HashMap 中的 Any 中恢复具体类型:(
    猜你喜欢
    • 1970-01-01
    • 2021-05-31
    • 1970-01-01
    • 2019-05-22
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多