I’ve been playing around with writing Redis Modules in Rust. This is my first attempt at using Rust FFI and bindings. How do I call this method and end up with a data value in Rust without destroying the Redis pointer?
extern "C" {
pub static mut RedisModule_GetTimerInfo: ::std::option::Option<
unsafe extern "C" fn(
ctx: *mut RedisModuleCtx,
id: RedisModuleTimerID,
remaining: *mut u64,
data: *mut *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int,
>;
}
See the RedisModule_GetTimerInfo API Docs for more details.
I ended up getting this to work, but it throws an error if I call it with the same id
twice:
let mut ms = 0 as u64;
let val = "";
let ptr = Box::into_raw(Box::new(&mut val)) as *mut *mut c_void;
let ok = unsafe { RedisModule_GetTimerInfo.unwrap()(ctx, id, &mut ms, ptr) };
let mut data: Option<String> = None;
if ok == 0 {
let val = unsafe { Box::from_raw(*ptr as *mut &str) };
// trim nul bytes
data = Some(val.trim_matches(char::from(0)).to_string());
}
This didn’t work because of how Box::from_raw
owns the raw pointer and the pointer is destroyed when the box is dropped.
I tried countless ways to make this work without using Box::into_raw
& Box::from_raw
and all of times they either end up crashing Redis or end up as a pointer that I don’t know how to convert to &str
.
Update: I originally had an example of using RedisModule_StopTimer
which was a mistake. Corrected to use the method I was asking about.
2
Answers
Using one of the links @Shepmaster added, I was finally able to figure this out. I swear I tried some variation of this but didn't think to try double boxing...
Here's what I did:
Thanks all for your help!
I’m one of the maintainers of the redismodule-rs crate, which provides a high-level Rust API for writing Redis modules.
Prompted by your question, I looked into adding these timer APIs to the crate in a safe manner, and will push the code to the repo once I’m done with it.
The following code shows how to retrieve the data safely: