rust FFI跨语言调用
在调用 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`给别的语言时:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
#[derive(Debug)] 这个`derive` 属性会自动创建所需的实现,使限定的`struct` 能使用 `fmt::Debug` 打印。
以上代码中完全不知道`foo`是一个什么东西。安全也无从说起了,只能靠文档。 因此在`ffi`调用时往往会丧
失掉`rust`类型系统带来的方便和安全。在这里提供一个小技巧:使用`Box>`来包装你的类型。
`rust`的`Any`类型为`rust`带来了运行时反射的能力,使用`Any`跨越`ffi`边界将极大提高程序安全性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }