In the context of programming with window callbacks on Window, I've fallen into the following situation:
I've a struct which I can control:
struct Foo {
// some custom fields
}
and a callback function whose the content is also controllable, but its type is fixed. This function will be called multiple times (with different parameters) by the system. There is only a single moment where param
is controllable, it is where msg
is MSG_INIT
.
unsafe extern "system" fn callback(msg: usize, param: isize) {
// some custom implementation
}
In the function, I need access/modify an instance of Foo
(actually some fields Foo
are initialized using information that the system passes to the callback
). I've come to the following code:
let mut foo = ...some Foo...
extern "system" fn callback(msg: usize, param: isize) {
static STORED_FOO_ADDR: OnceCell<isize>
match msg {
// this message is passed first, once by the system
// and this is the single moment where I can control the param,
// param will be a raw pointer to foo
MSG_INIT => {
// store address of foo
STORED_FOO_ADDR.set(param).unwrap();
// then use it as a &mut Foo
let foo = unsafe {
(param as *mut Foo).as_mut().unwrap()
};
// use foo
...
}
MSG_READ => {
// extract the raw pointer from the stored pointer
let foo = unsafe {
(*STORED_FOO_ADDR.get_unchecked().unwrap() as *const Foo).as_ref().unwrap()
};
// and use foo as &Foo
...
}
MSG_WRITE => {
// extract the raw pointer from the stored pointer
let foo = unsafe {
(*STORED_FOO_ADDR.get_unchecked().unwrap() as *mut Foo).as_mut().unwrap()
};
// and use foo a &mut Foo
...
}
}
}
// finally, use callback and foo
system_use_callback(callback, &mut foo as *mut _ as isize);
In summary, I've stored a raw pointer *mut Foo
to foo
, and cast back it as either &mut Foo
or &Foo
depending where I need to change the content of foo
or not.
While the code runs, I'm still worry about it. When I cast the raw pointer as &mut Foo
, then use it, the memory location of the owner may be changed.
MSG_WRITE => {
// extract the raw pointer from the stored pointer
let foo = unsafe {
(*STORED_FOO_ADDR.get_unchecked().unwrap() as *mut Foo).as_mut().unwrap()
};
// and use foo as &mut Foo
...
}
The next time when I cast the raw pointer as &Foo
,
MSG_READ => {
// extract the raw pointer from the stored pointer
let foo = unsafe {
(*STORED_FOO_ADDR.get_unchecked().unwrap() as *const Foo).as_ref().unwrap()
};
// and use foo as &Foo
...
}
the program may crash, or worst I may have another object which has nothing in common.
Is this case possible. If yes, how can I refactoring the code?.