RustFFI入门:extern、ABI与底层符号链接解析Rust以内存安全著称,但有时我们需要打破边界,与C/C++等外部代码交互,或者直接进行底层操作,这就是外部函数接口(FFI)的用武之地。作为一篇入门指南,我们将通过一个精简的Rust代码示例,聚焦于FFI的三大基石
Rust 以内存安全著称,但有时我们需要打破边界,与 C/C++ 等外部代码交互,或者直接进行底层操作,这就是 外部函数接口(FFI) 的用武之地。作为一篇入门指南,我们将通过一个精简的 Rust 代码示例,聚焦于 FFI 的三大基石:extern
块、调用约定(ABI) 和符号链接。学习如何使用它们,是您迈向 Rust 高级系统编程的关键第一步。
// Rust is highly capable of sharing FFI interfaces with C/C++ and other statically compiled
// languages, and it can even link within the code itself! It makes it through the extern
// block, just like the code below.
//
// The short string after the `extern` keyword indicates which ABI the externally imported
// function would follow. In this exercise, "Rust" is used, while other variants exists like
// "C" for standard C ABI, "stdcall" for the Windows ABI.
//
// The externally imported functions are declared in the extern blocks, with a semicolon to
// mark the end of signature instead of curly braces. Some attributes can be applied to those
// function declarations to modify the linking behavior, such as #[link_name = ".."] to
// modify the actual symbol names.
//
// If you want to export your symbol to the linking environment, the `extern` keyword can
// also be marked before a function definition with the same ABI string note. The default ABI
// for Rust functions is literally "Rust", so if you want to link against pure Rust functions,
// the whole extern term can be omitted.
//
// Rust mangles symbols by default, just like C++ does. To suppress this behavior and make
// those functions addressable by name, the attribute #[no_mangle] can be applied.
extern "Rust" {
fn my_demo_function(a: u32) -> u32;
#[link_name = "my_demo_function"]
fn my_demo_function_alias(a: u32) -> u32;
}
mod Foo {
// No `extern` equals `extern "Rust"`.
#[no_mangle]
fn my_demo_function(a: u32) -> u32 {
a
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
// The externally imported functions are UNSAFE by default
// because of untrusted source of other languages. You may
// wrap them in safe Rust APIs to ease the burden of callers.
//
// SAFETY: We know those functions are aliases of a safe
// Rust function.
unsafe {
my_demo_function(123);
my_demo_function_alias(456);
}
}
}
这段 Rust 代码详细展示了 外部函数接口(FFI) 在 Rust 内部的运用,主要围绕如何使用 extern
块进行函数声明和符号链接。
核心机制和作用:
这段代码通过使用 extern "Rust" { ... }
块来导入(或声明)函数签名,尽管这些函数的实际定义 (my_demo_function
) 存在于代码的另一个模块 Foo
内部。这种模式通常用于与其他语言(如 C/C++)或动态库进行交互,但在本例中,它被用于演示 Rust 内部的符号链接机制。
extern
块与 ABI: extern "Rust" { ... }
定义了一个外部块,其中 "Rust"
指定了调用约定(ABI)。这意味着被声明的函数将遵循 Rust 编译器默认的调用规则。其他常见的 ABI 包括 "C"
(标准 C 语言 ABI)或 "stdcall"
(Windows ABI)。#[no_mangle]
: 在 Foo
模块内部定义的函数 my_demo_function
前使用了 #[no_mangle]
属性。通常,Rust 会像 C++ 一样对函数名进行符号重整(Name Mangling),使其在二进制文件中难以直接通过名称查找。#[no_mangle]
阻止了这一行为,确保了函数在链接环境中有一个干净、可预测的名称 (my_demo_function
)。#[link_name]
: 在 extern
块中,my_demo_function_alias
明确使用了 #[link_name = "my_demo_function"]
属性。这告诉链接器,尽管函数在 Rust 代码中被命名为 my_demo_function_alias
,但它应该链接到外部环境中名为 my_demo_function
的实际符号上。extern
块中声明的函数,无论是链接到 C 代码还是像本例中链接到 Rust 代码,默认都被视为不安全(unsafe
)。这是因为编译器无法验证外部函数是否满足 Rust 的内存安全和并发安全规则。因此,在测试用例中,对 my_demo_function
及其别名的调用必须被包裹在 unsafe { ... }
块内,以此作为程序员对内存安全的承诺。总之,这段代码是理解 Rust FFI 和链接机制的基础示例,展示了如何使用 extern
、#[no_mangle]
和 #[link_name]
来控制函数符号的导入和导出,从而实现跨语言或跨模块的低级别通信。
这段代码是理解 Rust FFI 和符号链接机制的绝佳示例。它展示了三个核心要素:通过 extern
确定调用约定(ABI),通过 #[no_mangle]
导出清晰的函数符号,以及通过 #[link_name]
创建符号别名。
掌握这些属性,意味着您获得了在 Rust 安全框架下,与底层内存和外部代码进行高性能、低级别通信的能力。但请务必记住,任何 FFI 调用都是对内存安全的主动接管,必须严格履行安全契约。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!