使用Rust FFI时,如何初始化一个不透明的C结构体?

15

以下是我想在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`
3个回答

10
最安全的方法是自己初始化结构体:
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
@EFTH 我已经解决了这个问题。请查看我回答中关于“零大小”的部分。如果您有一个不透明指针,那么请参阅3个链接的问题。 - Shepmaster
请注意,std::mem::uninitialized自1.39.0版本起已被弃用,请改用mem::MaybeUninit - Lonami

3

根据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()
}

0

在阅读了Shepmaster的答案之后,我更仔细地查看了库的头文件。正如他们所说的那样,some_lib_struct_t只是指向actual_lib_struct_t的指针的typedef。我进行了以下更改:

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);
    }
}

它可以工作!但是我收到了警告:在外部模块中找到零大小结构体,请考虑向此结构体添加成员,#[warn(improper_ctypes)]默认开启


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接