我有一个现有的C ++程序,它使用Berkeley DB作为存储后端。我想在Rust中重写它。有没有办法在Rust中编写外部函数接口以使用Berkeley DB?我找到了教程Rust Foreign Function Interface,但对于BDB中使用的复杂C结构来说,这似乎太简单了。例如,打开数据库
我需要声明一个DB结构并调用DB->open()
。但我不知道如何使用教程中显示的示例来做到这一点。
任何人都可以帮忙吗?
答案 0 :(得分:3)
好吧,看看BDB的C API,我发现它由C结构和元素指向函数组成。它没有在教程中解释(这很奇怪),但Rust目前支持pointers to foreign functions。它也在Rust reference manual中提到。
您可以粗略地根据db.h
中定义的结构创建所有必需的结构,并且由于Rust和C结构的内存布局相同,您可以将这些结构传递给库或从库中传递这些结构并期望存在正确的指针在他们中间。
例如,您的DB->open()
电话可能如下所示:
struct DB {
open: extern "C" fn()
}
let db = ... // Get DB from somewhere
(db.open)() // Parentheses around db.open are needed to disambiguate field access
然而,这确实应该包含在某种基于impl
的接口中,因为调用extern函数是不安全的操作,并且您不希望用户将unsafe
置于所有数据库交互之外。
答案 1 :(得分:0)
鉴于DB结构的庞大规模和复杂性,似乎没有一种“干净”的方式将整个事物暴露给Rust。类似于C2HS从C头生成FFI的工具会很好,但是我们没有。{/ p>
另请注意,Rust FFI当前无法调用C ++库,因此您必须使用C API。
我根本不熟悉数据库API,但在C中创建一个小型支持库以实际创建数据库结构的实例,然后通过struct __db
公开成员,这似乎是合理的。 getter和setter函数。
您的实施可能如下所示:
[#link_args = "-lrust_dbhelper"]
extern {
fn create_DB() -> *c_void;
fn free_DB(db: *c_void);
}
struct DB {
priv db: *c_void
}
impl Drop for DB {
fn drop(&self) {
free_DB(self.db);
}
}
priv struct DBAppMembers {
pgsize: u32,
priority: DBCachePriority
// Additional members omitted for brevity
}
impl DB {
pub fn new() -> DB {
DB {
db: create_DB()
}
}
pub fn set_pgsize(&mut self, u32 pgsize) {
unsafe {
let x: *mut DBAppMembers = ::std::ptr::transmute(self.db);
x.pgsize = pgsize;
}
}
// Additional methods omitted for brevity
}
通过使用DB.db
成员作为参数专门调用C函数,您可以避免一些额外的工作,但这需要在不安全的上下文中工作,这应该尽可能避免。否则,libdb
导出的每个函数都需要在您的原生struct DB
中拥有自己的包装。