将对象存储在静态 Box 中,然后作为可变对象进行检索?

Store an object in a static Box and then retrieve as mutable?

提问人:mike rodent 提问时间:11/15/2023 最后编辑:mike rodent 更新时间:11/15/2023 访问量:42

问:

这是 PyO3 的情况。因此,Python 调用了一个长时间运行的任务。使用 ZeroMQ 消息传递(进程间消息传递*),用户可以命令暂停任务。在这一点上,我想将“处理框架”对象存储在一个框中,以便用户以后可能会单击“恢复”,这样,回到 Rust 中,在第二次调用时,框架对象可以从这个框中检索,并从它离开的地方再次开始工作。这样做的最大好处是可以保留框架的状态。staticstatic

令我惊讶的是,Rust 内存中的结构似乎在对 PyO3 函数的 Python 调用之间得以保留,这一事实启发了我尝试这一点。static

这就是我在检测“挂起”命令时“存放”框架的方式:

static SUSPENDED_FRAMEWORK_OPTION: OnceLock<Box<HandlingFramework>> = OnceLock::new();
...
let mut handling_framework = HandlingFramework::new(op_type, index_name, dir_root_path_str, current_app_version, current_index_version, tx_zero_client);
handling_framework.init()?; 
let (job_suspended_state, total_word_count, total_ldoc_count) = handling_framework.index_docs()?;
if std::matches!(job_suspended_state, JobSuspendedState::Suspended) {
    let _ = SUSPENDED_FRAMEWORK_OPTION.set(Box::new(handling_framework))
}

...这就是我尝试在以后的 PyO3 调用中检索框架的方式:

if op_type == "RESUME".to_string(){
    let option_for_framework = SUSPENDED_FRAMEWORK_OPTION.get();
    let framework_box_ref = option_for_framework.unwrap();
    info!("text_doc_hit_objs_for_processing remaining {}", framework_box_ref.text_doc_hit_objs_for_processing.len());
    info!("framework_box_ref type {}", str_type_of(&framework_box_ref));

framework_box_ref type &alloc::boxed::Box<populate_index::handling_framework::HandlingFramework>

从某种意义上说,这是有道理的,因为我能够获得对检索到的框架中各种对象的读取访问权限。

但是要做任何有用的事情(恢复处理),我需要对框架对象具有可变访问权限。我在那里尝试的一切都失败了,通常是沿着“无法移出共享引用”的思路。

我试过这样的事情:

fn unbox<T>(value: Box<T>) -> T {
    *value
}
let mut framework = unbox(*framework_box_ref);

这给出了:

error[E0507]: cannot move out of `*framework_box_ref` which is behind a shared reference
   --> src\lib.rs:114:29
    |
114 |         let mut framework = unbox(*framework_box_ref);
    |                                   ^^^^^^^^^^^^^^^^^^ move occurs because `*framework_box_ref` has type `Box<HandlingFramework>`, which does not implement the `Copy` trait

谁能建议如何获得这个可变变量?目前没有实施或...我已经尝试过这些,但是有太多的内部字段没有实现一个或两个。如果需要,我可能会“手动”实现一个或两个,但目前我希望在没有它的情况下获得可变访问......HandlingFrameworkCopyClonederive


* 实际上,这不是进程间消息传递,而是线程间消息传递:Rust 代码似乎与调用它的 Python 工作线程在同一个线程中运行。因此,使用另一个 Python 线程来发送消息......

rust 可变 pyo3 lazy-static

评论


答:

3赞 cdhowie 11/15/2023 #1

如果没有某种内部可变性,就无法写入 a 中的值,此时您应该使用它而不是 .请注意,无论如何都不能从引用中移动,甚至不能从 ;要做到这一点,你需要一些东西来交换,以便所指对象仍然有效。OnceLockOnceLock&mut

目前还不清楚为什么你需要一个。Box

请考虑改用 .您可以使用 Option::take 来“窃取” ,留下的内容。Mutex<Option<_>>OptionNone

static SUSPENDED_FRAMEWORK_OPTION: Mutex<Option<HandlingFramework>> = Mutex::new(None);

// ...

if std::matches!(job_suspended_state, JobSuspendedState::Suspended) {
    *SUSPENDED_FRAMEWORK_OPTION.lock().unwrap() = Some(handling_framework);
}

稍后,您可以像这样取出值:

let mut framework = SUSPENDED_FRAMEWORK_OPTION
    .lock()
    .unwrap()
    .take()
    .expect("SUSPENDED_FRAMEWORK_OPTION was None");

但是,我强烈建议不要使用这样的全局状态。最好将此值存储在表示框架状态的 Python 对象上,然后您可以从那里获取它,而不需要全局变量。

评论

0赞 mike rodent 11/15/2023
很有道理,谢谢。我通过使用 crate “log” 和 “log4rs” 配置日志发现了静态对象的这种持久性:第二个 PyO3 调用在尝试配置时生成了错误:“日志记录已配置”。但也许日志记录是证明规则的例外。事实上,我最初开发这个正是为了减少变量的使用。好吧,总是很糟糕(几乎)。HandlingFrameworkstructstaticstatic