Skip to content

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 --release

C 代码调用 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);
}

最佳实践

  1. 使用 #[repr(C)]:确保 Rust 结构体的布局与 C 结构体一致。

  2. 处理 null 指针:始终检查从 C 接收的指针是否为 null。

  3. 管理内存:确保正确分配和释放内存,避免内存泄漏。

  4. 处理错误:提供清晰的错误处理机制。

  5. 文档化:清楚地记录 FFI 接口的使用方法。

总结

  • FFI:允许 Rust 与其他语言交互的机制
  • 从 Rust 调用 C:使用 extern "C" 声明外部函数
  • 从 C 调用 Rust:使用 #[no_mangle]extern "C" 导出函数
  • 字符串处理:使用 CStringCStr 处理字符串
  • 结构体处理:使用 #[repr(C)] 确保结构体布局一致
  • 指针处理:正确处理指针和内存管理

FFI 是 Rust 中一个强大的特性,它允许 Rust 与其他语言无缝集成。通过本章节的学习,你应该已经了解了 Rust 中 FFI 的基本概念和使用方法。