【问题标题】:How do I initialize an opaque C struct when using Rust FFI?使用 Rust FFI 时如何初始化不透明的 C 结构?
【发布时间】:2017-04-11 15:25:26
【问题描述】:

这是我想在 C 代码中执行的操作:

#include <some_lib.h>
int main() {
    some_lib_struct_t x;
    some_lib_func(&x);
}

如何使用 Rust 中的库?到目前为止,这是我所得到的:

extern crate libc; // 0.2.51

struct some_lib_struct_t;

#[link(name = "some_lib")]
extern "C" {
    fn some_lib_func(x: *mut some_lib_struct_t);
}

fn main() {
    let mut x: some_lib_struct_t;
    unsafe {
        some_lib_func(&mut x);
    }
}

编译时出现错误:

error[E0381]: borrow of possibly uninitialized variable: `x`
  --> src/main.rs:13:23
   |
13 |         some_lib_func(&mut x);
   |                       ^^^^^^ use of possibly uninitialized `x`

【问题讨论】:

    标签: rust ffi


    【解决方案1】:

    最安全的答案是自己初始化结构体:

    let mut x: some_lib_struct_t = some_lib_struct_t;
    unsafe {
        some_lib_func(&mut x);
    }
    

    最接近C代码的模拟是使用MaybeUninit

    use std::mem::MaybeUninit;
    
    unsafe {
        let mut x = MaybeUninit::uninit();
        some_lib_func(x.as_mut_ptr());
    }
    

    Rust 1.36 之前,你可以使用mem::uninitialized

    unsafe {
        let mut x: some_lib_struct_t = std::mem::uninitialized();
        some_lib_func(&mut x);
    }
    

    必须确保 some_lib_func 完全初始化结构的所有成员,否则不安全将泄漏到 unsafe 块之外。

    说到“结构的成员”,我几乎可以保证你的代码不会做你想做的事。您已将 some_lib_struct_t 定义为大小为零。这意味着不会为其分配任何堆栈空间,并且对它的引用不会是您的 C 代码所期望的。

    您需要在 Rust 中镜像 C 结构的定义,以便可以分配适当的大小、填充和对齐方式。通常,这意味着使用repr(C)

    很多时候,C 库通过始终返回指向不透明类型的指针来避免暴露其内部结构表示:

    【讨论】:

    • 我使用 mem::uninitialized() 让它工作了。但是我收到了编译器的警告。我用更多信息更新了问题。
    • @EFTH 我已经解决了这个问题。请参阅我的回答中关于“零大小”的部分。如果您有不透明指针,请查看 3 个链接问题。
    • 请注意,std::mem::uninitialized自 1.39.0 起已弃用:请改用 mem::MaybeUninit
    【解决方案2】:

    来自mem::uninitialized() 的文档:

    自 1.39.0 起已弃用:改用 mem::MaybeUninit

    新的解决方案如下所示:

    use std::mem::MaybeUninit;
    
    let instance = unsafe {
        let mut x: MaybeUninit<some_lib_struct_t> = MaybeUninit::uninit();
        some_lib_func(x.as_mut_ptr());
        x.assume_init()
    }
    

    【讨论】:

      【解决方案3】:

      阅读Shepmaster's answer 后,我仔细查看了库的标题。正如他们所说,some_lib_struct_t 只是指向actual_lib_struct_t 的指针的类型定义。我做了以下更改:

      extern crate libc;
      
      struct actual_lib_struct_t;
      type some_lib_type_t = *mut actual_lib_struct_t;
      
      #[link(name="some_lib")]
      extern {
          fn some_lib_func(x: *mut some_lib_type_t);
      }
      
      fn main() {
          let mut x: some_lib_type_t;
          unsafe {
              x = std::mem::uninitialized();
              some_lib_func(&mut x);
          }
      }
      

      而且它有效!但是我确实收到了警告found zero-size struct in foreign module, consider adding a member to this struct, #[warn(improper_ctypes)] on by default

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-27
        • 2021-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-16
        相关资源
        最近更新 更多