Appearance
Rust FFI
FFI(Foreign Function Interface)是 Rust 中用于与其他语言交互的机制。本章节将介绍 Rust 中的 FFI。
什么是 FFI
FFI 允许 Rust 代码调用其他语言的函数,也允许其他语言调用 Rust 代码。
从 Rust 调用 C 函数
定义外部函数
使用 extern 关键字定义外部函数:
rust
// 声明外部 C 函数
#[link(name = "c")]
extern "C" {
fn printf(format: *const i8, ...) -> i32;
fn puts(s: *const i8) -> i32;
}
fn main() {
let hello = "Hello, FFI!\0"; // C 字符串需要以 null 结尾
unsafe {
printf(b"Hello, %s!\n\0" as *const u8 as *const i8, hello.as_ptr() as *const i8);
puts(hello.as_ptr() as *const i8);
}
}编译和运行
bash
rustc ffi_example.rs -o ffi_example
./ffi_example从 C 调用 Rust 函数
导出 Rust 函数
使用 #[no_mangle] 和 extern "C" 来导出 Rust 函数:
rust
// lib.rs
#[no_mangle]
extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
#[no_mangle]
extern "C" fn greet(name: *const u8) {
let c_str = unsafe { std::ffi::CStr::from_ptr(name) };
let rust_str = c_str.to_str().unwrap();
println!("Hello, {}!", rust_str);
}编译为动态库
bash
cargo new --lib rust_ffi
cd rust_ffi
# 修改 Cargo.toml
# [lib]
# crate-type = ["cdylib"]
cargo build --releaseC 代码调用 Rust 函数
c
// main.c
#include <stdio.h>
// 声明 Rust 函数
extern int add(int a, int b);
extern void greet(const char* name);
int main() {
int result = add(5, 3);
printf("5 + 3 = %d\n", result);
greet("World");
return 0;
}编译和运行
bash
gcc main.c -L target/release -lrust_ffi -o c_example
./c_example处理字符串
Rust 字符串转 C 字符串
rust
use std::ffi::CString;
#[no_mangle]
extern "C" fn get_hello() -> *const u8 {
let c_str = CString::new("Hello from Rust").unwrap();
c_str.into_raw()
}
// 注意:需要提供一个函数来释放内存
#[no_mangle]
extern "C" fn free_string(s: *const u8) {
unsafe {
if !s.is_null() {
CString::from_raw(s as *mut u8);
}
}
}C 字符串转 Rust 字符串
rust
use std::ffi::CStr;
#[no_mangle]
extern "C" fn process_string(s: *const u8) {
unsafe {
if !s.is_null() {
let c_str = CStr::from_ptr(s);
match c_str.to_str() {
Ok(rust_str) => println!("Received: {}", rust_str),
Err(e) => println!("Error: {}", e),
}
}
}
}处理结构体
Rust 结构体和 C 结构体
rust
// Rust 中的结构体
#[repr(C)]
struct Point {
x: f64,
y: f64,
}
#[no_mangle]
extern "C" fn distance(p1: Point, p2: Point) -> f64 {
let dx = p1.x - p2.x;
let dy = p1.y - p2.y;
(dx * dx + dy * dy).sqrt()
}处理指针
传递数组
rust
#[no_mangle]
extern "C" fn sum_array(arr: *const i32, len: usize) -> i32 {
unsafe {
let slice = std::slice::from_raw_parts(arr, len);
slice.iter().sum()
}
}传递回调函数
rust
type Callback = extern "C" fn(i32);
#[no_mangle]
extern "C" fn call_callback(callback: Callback, value: i32) {
callback(value);
}最佳实践
使用
#[repr(C)]:确保 Rust 结构体的布局与 C 结构体一致。处理 null 指针:始终检查从 C 接收的指针是否为 null。
管理内存:确保正确分配和释放内存,避免内存泄漏。
处理错误:提供清晰的错误处理机制。
文档化:清楚地记录 FFI 接口的使用方法。
总结
- FFI:允许 Rust 与其他语言交互的机制
- 从 Rust 调用 C:使用
extern "C"声明外部函数 - 从 C 调用 Rust:使用
#[no_mangle]和extern "C"导出函数 - 字符串处理:使用
CString和CStr处理字符串 - 结构体处理:使用
#[repr(C)]确保结构体布局一致 - 指针处理:正确处理指针和内存管理
FFI 是 Rust 中一个强大的特性,它允许 Rust 与其他语言无缝集成。通过本章节的学习,你应该已经了解了 Rust 中 FFI 的基本概念和使用方法。