在 Rust 中声明一个结构或变量的正确方法是什么,它可以传递给需要指针的 C 代码?
What is the correct way to declare a structure, or just a variable, in Rust that can be passed to C code expecting a pointer?
我很难集中精力声明可变(或指针)变量并通过 FFI 与 C 代码交互。我一天中的大部分时间都在玩这个,并且由于 Rust 的发展速度太快而发现了相互矛盾的例子。 :)
情况是这样的:我有一个 C 函数,它接受一个指向 struct
的指针,这个结构有 int
s 和 char *
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;
}
显然实际代码更复杂,我只是想通过一个基本示例来理解 mutability/pointers 如何在 Rust 中使用 FFI。
What is the correct way to declare a structure, or just a variable, in Rust that can be passed to C code expecting a pointer?
结构的内存布局是未定义的(例如,允许编译器对字段重新排序),除非您将 #[repr(C)]
属性添加到结构。此属性为结构提供与 C 兼容的布局。
#[repr(C)]
struct TestStruct {
an_int: libc::c_int,
a_string: *mut libc::c_char
}
在结构中使用原始指针工作正常,但我们可以做得更好。 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,即它们与指针一起携带额外的数据。例如,切片(*const [T]
、*mut [T]
、&'a [T]
、&'a mut [T]
)可以被认为是一个结构或元组,包含指向第一项的指针和项的数量在切片中(a usize
);特征对象(*const T
、*mut T
、&'a T
、&'a mut T
其中 T
是特征的名称)由指向对象的指针和指针组成到特征实现的虚方法table。在定义与 C 结构匹配的 Rust 结构时,您应该避免使用这些类型。
您可以在 the FFI section of the Rust book 中找到有关使用 Rust 的 FFI 的更多信息。
我很难集中精力声明可变(或指针)变量并通过 FFI 与 C 代码交互。我一天中的大部分时间都在玩这个,并且由于 Rust 的发展速度太快而发现了相互矛盾的例子。 :)
情况是这样的:我有一个 C 函数,它接受一个指向 struct
的指针,这个结构有 int
s 和 char *
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;
}
显然实际代码更复杂,我只是想通过一个基本示例来理解 mutability/pointers 如何在 Rust 中使用 FFI。
What is the correct way to declare a structure, or just a variable, in Rust that can be passed to C code expecting a pointer?
结构的内存布局是未定义的(例如,允许编译器对字段重新排序),除非您将 #[repr(C)]
属性添加到结构。此属性为结构提供与 C 兼容的布局。
#[repr(C)]
struct TestStruct {
an_int: libc::c_int,
a_string: *mut libc::c_char
}
在结构中使用原始指针工作正常,但我们可以做得更好。 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,即它们与指针一起携带额外的数据。例如,切片(*const [T]
、*mut [T]
、&'a [T]
、&'a mut [T]
)可以被认为是一个结构或元组,包含指向第一项的指针和项的数量在切片中(a usize
);特征对象(*const T
、*mut T
、&'a T
、&'a mut T
其中 T
是特征的名称)由指向对象的指针和指针组成到特征实现的虚方法table。在定义与 C 结构匹配的 Rust 结构时,您应该避免使用这些类型。
您可以在 the FFI section of the Rust book 中找到有关使用 Rust 的 FFI 的更多信息。