使用 Rust FFI 时如何初始化不透明的 C 结构?
How do I initialize an opaque C struct when using Rust FFI?
这是我想用 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`
最安全的答案是自己初始化结构:
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
块之外。
说到 "members of the struct",我几乎可以保证您的代码不会执行您想要的操作。您已将 some_lib_struct_t
定义为零大小。这意味着不会为它分配堆栈 space,并且对它的引用不会是您的 C 代码所期望的。
您需要在 Rust 中镜像 C 结构的定义,以便可以分配适当的大小、填充和对齐方式。通常,这意味着使用 repr(C)
.
很多时候,C 库通过始终返回指向不透明类型的指针来避免暴露其内部结构表示:
- In Rust how can I define or import a C struct from a third party library?
看完Shepmaster's answer后,我仔细看了看图书馆的header。正如他们所说,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
.
来自 mem::uninitialized()
的文档:
Deprecated since 1.39.0: use mem::MaybeUninit
instead
新的解决方案如下所示:
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()
}
这是我想用 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`
最安全的答案是自己初始化结构:
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
块之外。
说到 "members of the struct",我几乎可以保证您的代码不会执行您想要的操作。您已将 some_lib_struct_t
定义为零大小。这意味着不会为它分配堆栈 space,并且对它的引用不会是您的 C 代码所期望的。
您需要在 Rust 中镜像 C 结构的定义,以便可以分配适当的大小、填充和对齐方式。通常,这意味着使用 repr(C)
.
很多时候,C 库通过始终返回指向不透明类型的指针来避免暴露其内部结构表示:
- In Rust how can I define or import a C struct from a third party library?
看完Shepmaster's answer后,我仔细看了看图书馆的header。正如他们所说,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
.
来自 mem::uninitialized()
的文档:
Deprecated since 1.39.0: use
mem::MaybeUninit
instead
新的解决方案如下所示:
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()
}