在Rust中声明一个结构,或者只是一个变量的正确方法是什么,可以传递给期望指针的C代码?

时间:2016-03-23 01:30:44

标签: c rust ffi

我很难绕过声明可变(或指针)变量并通过FFI与C代码交互。我大部分时间都在玩这个,并且由于Rust的发展速度很快,我发现了相互矛盾的例子。 :)

情况是这样的:我有一个C函数,它接收一个指向struct的指针,这个结构的字段是intchar * s。我的理解是我需要在Rust中声明一个类似的结构来传递给extern C函数。

以下是我在尝试解决此问题时编写的示例文件:

main.rs
extern crate libc;

struct testStruct {
  an_int: libc::c_int,
  a_string: *mut libc::c_char
}

extern {
  fn start_test(test: *mut testStruct) -> libc::c_int;
}

fn main() {
  // println!("Hello, world!");
  let test_struct = testStruct { an_int: 1, a_string: "hello" };
  start_test(&mut test_struct);
}

-

test_file.c
#include <stdio.h>
#include "test_file.h"

struct test_struct {
    int an_int;
    char *a_string;
};

int start_client(struct test_struct *test) {
    printf("Test function!\n");
    return 0;
}

显然,实际的代码更复杂,我只想尝试一个基本的例子来理解可变性/指针如何在Rust中使用FFI。

1 个答案:

答案 0 :(得分:5)

  

在Rust中声明一个可以传递给期望指针的C代码的结构或变量的正确方法是什么?

结构的内存布局是未定义的(例如,允许编译器重新排序字段),除非您将#[repr(C)]属性添加到结构中。此属性为结构提供了与C。

兼容的布局
#[repr(C)]
struct TestStruct {
    an_int: libc::c_int,
    a_string: *mut libc::c_char
}

在struct中使用原始指针可以正常工作,但我们可以做得更好。 Rust中还有另外两个重要类型,它们只由一个指针组成:借用指针(&'a T&'a mut T)和Box<T>。您可以使用这些类型代替*const T*mut T来明确指针借用现有值(并使编译器验证指针不会超过其指示对象)或指向当指针(或包含它的结构)超出范围时应该删除的堆上的对象。但是,请注意Box<T>,因为在C代码仍有指向值的指针时,您可能会意外释放值。

#[repr(C)]
struct TestStruct<'a> {
    an_int: libc::c_int,
    a_string: &'a mut libc::c_char
}

需要注意的另一件事是使用脂肪指针。在Rust中,一些指针类型是 fat <​​/ em>,即它们携带附加数据和指针。例如,切片(*const [T]*mut [T]&'a [T]&'a mut [T])可以被视为包含指向第一个项目和项目数量的指针的结构或元组在切片中(a usize);特征对象(*const T*mut T&'a T&'a mut T其中T是特征的名称)由指向对象的指针和指针组成到特征实现的虚方法表。在定义与C结构匹配的Rust结构时,应避免使用这些类型。

您可以在the FFI section of the Rust book中找到有关使用Rust&#FFI的更多信息。