I am learning rust and I've been trying to rewrite a project that I did in C# in rust, and I got stuck trying to access global state from a callback,
Is there a simple way to do that in rust? Keeping in mind the I can't add new parameter to the callback.
eg:
use std::collections::HashMap;
use std::time::instant;
use lib::bar;
struct Struct2{
foo: bar,
word: String,
}
struct GlobalState{
running: bool,
now: Instant,
map: HashMap<String, Struct2>,
hook_id: usize,
current_id: String,
}
impl GlobalState{
fn init() -> Self{
let hook_id = unsafe {set_ext_hook(HOOK_ID, system_hook)};
// Omitted initialization code.
}
// Omitted state mutation functions.
}
unsafe extern "system" fn system_hook(ext_param1:usize, ext_param2: usize) -> isize {
// use global state here
}
I tried using crates such as lazy_static and once_cell, but they didn't work because the external struct that I use (lib::bar in this example) "cannot be sent between threads safely"
My code so far is single threaded (I plan on using a different thread for the program's gui when I implement it)
Any help is appreciated, thanks.
You seem to be dealing with data that is neither Send
nor Sync
, so Rust won't allow you to place it in a global, even inside a mutex. It's not clear from the question whether this is a result of lib::bar
being genunely thread-unsafe, or just the unintended consequence of its use of raw pointers under the hood. It is also unclear whether you are in the position to modify lib::bar
to make its types Send
and Sync
.
Assuming most conservatively that lib::bar
cannot be changed, and taking into account that your program is single-threaded, your only safe option is to create a thread-local state:
use std::cell::RefCell;
use std::thread_local;
struct Foo(*const i32); // a non-Send/Sync type
struct GlobalState {
foo: Foo,
data: String,
mutable_data: RefCell<String>,
}
thread_local! {
static STATE: GlobalState = GlobalState {
foo: Foo(std::ptr::null()),
data: "bla".to_string(),
mutable_data: RefCell::new("".to_string()),
};
}
You can access that state (and modify its interior-mutable pieces) from any function:
fn main() {
STATE.with(|state| {
assert_eq!(state.foo.0, std::ptr::null());
assert_eq!(state.data, "bla");
assert_eq!(state.mutable_data.borrow().as_str(), "");
state.mutable_data.borrow_mut().push_str("xyzzy");
});
STATE.with(|state| {
assert_eq!(state.mutable_data.borrow().as_str(), "xyzzy");
});
}
Note that if you try to access the "global" state from different threads, each will get its own copy of the state:
fn main() {
STATE.with(|state| {
state.mutable_data.borrow_mut().push_str("xyzzy");
});
std::thread::spawn(|| {
STATE.with(|state| {
// change to "xyzzy" happened on the other copy
assert_eq!(state.mutable_data.borrow().as_str(), "");
})
})
.join()
.unwrap();
}