【发布时间】:2020-04-01 03:15:11
【问题描述】:
我一直在尝试用 Rust 编写 Redis 模块。这是我第一次尝试使用 Rust FFI 和绑定。如何在不破坏 Redis 指针的情况下调用此方法并在 Rust 中得到一个数据值?
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,
>;
}
有关详细信息,请参阅RedisModule_GetTimerInfo API Docs。
我最终让它工作了,但如果我用相同的id 调用它两次,它会引发错误:
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());
}
这不起作用,因为Box::from_raw 拥有原始指针,并且当盒子被丢弃时指针被销毁。
在不使用Box::into_raw 和Box::from_raw 的情况下,我尝试了无数种方法来完成这项工作,而且它们总是导致Redis 崩溃或最终成为我不知道如何转换为&str 的指针。
更新:我最初有一个使用 RedisModule_StopTimer 的示例,这是一个错误。更正为使用我询问的方法。
【问题讨论】:
-
&str是一个胖指针,它永远无法放入*mut c_void。你需要添加更多的间接性,如How do I pass a closure through raw pointers as an argument to a C function?所示; How do I convert a Rust closure to a C-style callback?; Segmentation fault when using C callback user data to store a boxed Rust closure. -
我想知道你是如何在 C 中得到
data: *mut *mut ::std::os::raw::c_void,的。它看起来像糟糕的 C 代码 -
不清楚您是如何尝试使用此 API 的。通过浏览文档,您似乎需要将原始指针传递给
RedisModule_CreateTimer,并且当您使用非空data指针调用RedisModule_CreateTimerInfo时,它会将原始指针复制到指针中。那么...您使用什么值来创建计时器?您链接到的函数与问题中的代码示例之间有什么联系? (你是不是不小心链接错了?) -
@trentcl 对不起,是的。你是对的。我已经更正了这个例子。
RedisModule_StopTimer使用*mut *mut c_void执行类似的操作来读取数据。