rust FFI跨语言调用

1. 手动绑定 C 库

在调用 C 库时,需要重新在 Rust 中对该 C 库中的数据类型和函数签名进行封装.

2. 将Rust编译成库

为了能让rust的函数通过ffi被调用,需要加上extern "C"对函数进行修饰。但由于rust支持重载,

所以函数名会被编译器进行混淆,就像c++一样。因此当你的函数被编译完毕后,函数名会带

上一串表明函数签名的字符串。比如:fn test() {}会变_ZN4test20hf06ae59e934e5641haaE. 这样

的函数名为ffi调用带来了困难,因此,rust提供了#[no_mangle]属性为函数修饰 对于带有

#[no_mangle]属性的函数,rust编译器不会为它进行函数名混淆。如

## 指定`crate`类型

 

`rustc`默认编译产生`rust`自用的`rlib`格式库,要让`rustc`产生动态链接库或者静态链接库,需要显式指定。

 

1. 方法1: 在文件中指定。 在文件头加上`#![crate_type = "foo"]`, 其中`foo`的可选类型有`bin`, `lib`, `rlib`, `dylib`, `staticlib`.

分别对应可执行文件, 默认(将由`rustc`自己决定), `rlib`格式,动态链接库,静态链接库。

2. 方法2: 编译时给rustc `--crate-type`参数。参数内容同上。

3. 方法3: 使用cargo,指定`crate-type = ["foo"]`, `foo`可选类型同1

## 小技巧: `Any`

由于在跨越`ffi`过程中,`rust`类型信息会丢失,比如当用`rust`提供一个`OpaqueStruct`给别的语言时:

 

 1 #[derive(Debug)]
 2 struct Foo {
 3   t: T
 4 }
 5 
 6 #[no_mangle]
 7 extern "C" fn new_foo_vec() -> *const c_void {
 8     Box::into_raw(Box::new(Foo {t: vec![1,2,3]})) as *const c_void
 9 }
10 
11 #[no_mangle]
12 extern "C" fn new_foo_int() -> *const c_void {
13     Box::into_raw(Box::new(Foo {t: 1})) as *const c_void
14 }
15 
16 fn push_foo_element(t: &mut Foo<Vec<i32>>) {
17     t.t.push(1);
18 }
19 
20 #[no_mangle]
21 extern "C" fn push_foo_element_c(foo: *mut c_void){
22     let foo2 = unsafe {
23         &mut *(foo as *mut Foo<Vec<i32>>) // 这么确定是Foo>? 万一foo是Foo怎么办?
24     };
25     push_foo_element(foo3);
26 }
View Code

 #[derive(Debug)] 这个`derive` 属性会自动创建所需的实现,使限定的`struct` 能使用 `fmt::Debug` 打印。

以上代码中完全不知道`foo`是一个什么东西。安全也无从说起了,只能靠文档。 因此在`ffi`调用时往往会丧

失掉`rust`类型系统带来的方便和安全。在这里提供一个小技巧:使用`Box>`来包装你的类型。

 `rust``Any`类型为`rust`带来了运行时反射的能力,使用`Any`跨越`ffi`边界将极大提高程序安全性。

 1 use std::mem::transmute;
 2 use std::any::Any;
 3 #[derive(Debug)]
 4 struct Foo{
 5   t: T
 6 }
 7 
 8 #[no_mangle]
 9 extern "C" fn new_foo_vec() -> *const c_void {
10     Box::into_raw(Box::new(Box::new(Foo {t: vec![1,2,3]}) as Box)) as *const c_void
11 }
12 
13 #[no_mangle]
14 extern "C" fn new_foo_int() -> *const c_void {
15     Box::into_raw(Box::new(Box::new(Foo {t: 1}) as Box)) as *const c_void
16 }
17 
18 fn push_foo_element(t: &mut Foo<Vec<i32>>) {
19     t.t.push(1);
20 }
21 
22 #[no_mangle]
23 extern "C" fn push_foo_element_c(foo: *mut c_void){
24     let foo2 = unsafe {
25         &mut *(foo as *mut Box)
26     };
27     let foo3: Option<&mut Foo<Vec<i32>>> = foo2.downcast_mut(); // 如果foo2不是*const Box>>, 则foo3将会是None
28     if let Some(value) = foo3 {
29       push_foo_element(value);
30     }
31 }
View Code

 

posted @ 2023-02-28 19:56  PKICA  阅读(365)  评论(0编辑  收藏  举报