我正在为C库编写一个包装器/ FFI,它需要在主线程中进行全局初始化调用以及一个用于销毁的调用.
以下是我目前处理它的方式:
struct App;
impl App {
fn init() -> Self {
unsafe { ffi::InitializeMyCLib(); }
App
}
}
impl Drop for App {
fn drop(&mut self) {
unsafe { ffi::DestroyMyCLib(); }
}
}
Run Code Online (Sandbox Code Playgroud)
可以使用如下:
fn main() {
let _init_ = App::init();
// ...
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,但感觉就像一个黑客,将这些调用绑定到不必要的结构的生命周期.在finally(Java)或at_exit(Ruby)块中使用析构函数似乎在理论上更合适.
在Rust中有更优雅的方法吗?
编辑
是否可以/安全地使用此设置(使用lazy_static包),而不是上面的第二个块:
lazy_static! {
static ref APP: App = App::new();
}
Run Code Online (Sandbox Code Playgroud)
是否可以保证在任何其他代码之前初始化此引用并在退出时销毁?lazy_static在图书馆使用是不好的做法?
这也可以通过这个结构更方便地访问FFI,因为我不必费心地传递对实例化结构的引用(_init_在我的原始示例中调用).
这也会使它在某些方面更安全,因为我可以将Appstruct默认构造函数设为私有.
我不知道除了措辞强硬的文档之外,没有办法强制在主线程中调用方法。因此,忽略该要求...:-)
一般来说,我会使用std::sync::Once,它似乎基本上是为这种情况设计的:
一种同步原语,可用于运行一次性全局初始化。对于 FFI 或相关功能的一次性初始化很有用。该类型只能用
ONCE_INIT值来构造。
请注意,没有任何清理措施;很多时候你只需要泄露图书馆所做的一切。通常,如果一个库有专用的清理路径,它也会被构造为将所有初始化数据存储在一个类型中,然后将其作为某种上下文或环境传递到后续函数中。这可以很好地映射到 Rust 类型。
警告
您当前的代码并不像您希望的那样具有保护性。由于您App是一个空结构,因此最终用户可以在不调用您的方法的情况下构造它:
let _init_ = App;
Run Code Online (Sandbox Code Playgroud)
我们将使用零大小的参数来防止这种情况。另请参见定义指向 C 不透明指针的字段的 Rust 习惯用法是什么?了解为 FFI 构造不透明类型的正确方法。
总而言之,我会使用这样的东西:
let _init_ = App;
Run Code Online (Sandbox Code Playgroud)