推荐的方法来包装C lib初始化/销毁例程

Jac*_*own 6 rust

我正在为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默认构造函数设为私有.

She*_*ter 4

我不知道除了措辞强硬的文档之外,没有办法强制在主线程中调用方法。因此,忽略该要求...:-)

一般来说,我会使用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)