Appearance
34.1 什么是 FFI
所谓的 FFI 指的是:
A foreign function interface(FFI)is a mechanism by which a program written in one programming language can call routines or make use of services written in another.
—— Wikipedia
Rust 不支持源码级别与其他语言的交互,因为这么做代价很大、限制太多,所以需要用库的方式来互相调用。这就要求我们有能力用 Rust 生成与 C 的 ABI 兼容的库。通过rustc -h
命令我们可以看到,Rust 编译器支持生成这样一些种类的库:
rust
--crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
Comma separated list of types of crates for the
compiler to emit
其中,cdylib 和 staticlib 就是与 C 的 ABI 兼容的。分别代表动态链接库和静态链接库。在编译的时候,我们需要指定这样的选项才能生成合适的目标文件。指定目标文件类型有两种方式:
在编译命令行中指定,如
rustc --crate-type=staticlib test.rs
;在源代码入口中指定,如
#![crate_type = "staticlib"]
。
另外,我们还需要注意,C 的 ABI 以及运行时库也不是完全统一的。此事是由 rustup 工具管理的。执行rustup show
可以看到当前使用的工具链是什么,比如笔者当前的工具链是:
rust
Default host: x86_64-unknown-linux-gnu
这意味着用这套工具链生成的 C 库是和 gcc 工具链的 ABI 兼容的。如果读者需要生成与 MSVC 的 ABI 兼容的库,那么需要使用:
rust
rustup target add x86_64-pc-windows-msvc
我们还可以用 rustup 来下载 Android 系统的工具链,实现交叉编译,等等。关于工具链以及 C 运行时库的链接方式的问题,读者可以参考 rustup 的官方网站。
除了指定目标文件、工具链之外,更重要的是需要注意接口的设计。不是所有的 Rust 的语言特性都适合放到交互接口中的。比如,Rust 中有泛型,C 语言里面没有,所以泛型这种东西是不可能暴露出来给 C 语言使用的,这就不是 C 语言的 ABI 的一部分。只有符合 C 语言的调用方式的函数,才能作为 FFI 的接口。这样的函数有以下基本要求:
使用
extern "C"
修饰,在 Rust 中extern fn
默认等同于extern "C" fn
;使用
#[no_mangle]
修饰函数,避免名字重整;函数参数、返回值中使用的类型,必须在 Rust 和 C 里面具备同样的内存布局。
下面我们用示例来说明如何实现 FFI。