rust 从mutex取得的指涉'值没有足够的存留时间

gv8xihay  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(139)

我正在学习Rust,并试图创建一个像egui和多线程的管理面板;一个线程更新散列表中的学生结构体,另一个线程运行egui并显示学生信息;这里的问题,我有:
第一个
下面是实现的完整代码:

pub struct Maestro<'a> {
    pub students: Arc<HashMap<PathBuf, Mutex<Student>>>,
    pub logger: Logger,
    pub actual_student: Option<&'a Mutex<Student>>,
    // Would like to make like this: pub student: Option<&Student> but cannot get reference to
    // Student from mutex's value
}

impl<'a> Maestro<'a> {
    pub fn new() -> Self {
        let config = load_config();
        let watcher = config.watcher;
        let students = Arc::new(watcher.init());
        let mut students_for_thread = Arc::clone(&students);
        std::thread::spawn(move || {
            watcher.run(&mut students_for_thread);
        });
        Maestro {
            students,
            logger: config.logger,
            actual_student: None,
        }
    }
}

impl<'a> eframe::App for Maestro<'a> {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            for (path, student) in self.students.iter() {
                if let Ok(mutex) = student.try_lock() {
                    ui.horizontal(|ui| {
                        ui.label(&mutex.name);
                        match &mutex.bashrc_editable {
                            true => ui.colored_label(egui::Color32::GREEN, "true"),
                            false => ui.colored_label(egui::Color32::RED, "false"),
                        };
                        if ui.button("See more").clicked() {
                            if self.students.contains_key(path) {
                                self.actual_student = Some(self.students.get(path.deref()).unwrap());
                                // FIXME 10/27/22 ectaclick: lifetime may not live long enough assignment requires that `'1` must outlive `'a`
                            }
                        }
                    });
                }
            }
        });
        // Would like to create a 'popup' on the right of the list
        if self.actual_student.is_some() {
            let student = self.actual_student.unwrap().lock().unwrap();
            egui::SidePanel::right("student").show(ctx, |ui| {
                ui.label(student.name.as_str());
                match student.bashrc_editable {
                    true => ui.colored_label(Color32::GREEN, "true"),
                    false => ui.colored_label(Color32::RED, "false"),
                }
            });
        }
        std::thread::sleep(Duration::from_millis(100));
    }

我试了许多式actual_student

Option<Student>
Box<Student>
Option<&Student>

尝试复制值,但actual_studentHashmap中的Student不同,Hashmap是由第二个执行绪更新的Student结构。
但问题还是一样。

5f0d552i

5f0d552i1#

你会得到一个生存期错误,因为如果这个值从hashmap中删除,你在actual_student中的引用就会无效。
对此问题的直接解决方案是使用Arc<Mutex<Student>>而不是Mutex<Student>
使用时只需使用Arc对象的克隆,这是相对轻量级的,因为它不是复制整个对象。
但一般来说,你是在尝试用OOP来思考。一个更好的方法是使用id,例如:

actual_student_id : Option<u64>

也就是说你得用

students.iter().find(|it|it.id == actual_student_id)

每次你都想得到一个实际学生的参考,但这避免了使用Arc,而且可能也会摆脱互斥锁,这甚至更好,因为互斥锁通常是性能打击的来源,有时很难调试。

相关问题