GitHunt
LI

liweinan/rust_c_ffi_sample

Rust/C 互操作样例项目

基于 Rust 与 C 互操作的示例,覆盖三种场景:C 调用 RustRust 调用 C内联汇编。例子精简,仅说明机制。

前置条件

  • Rust(rustup 安装)
  • gcc、make
  • 本机运行需 x86_64 才能执行内联汇编示例(非 x86_64 时 asm_demo 使用纯 Rust 实现,测试仍通过)
  • 可选:Docker(x86_64 镜像,用于一致环境与 CI)

项目结构

rust_c_ffi_sample/
├── Cargo.toml                 # workspace
├── README.md
├── Dockerfile                  # x86_64: rust + gcc + make
├── scripts/
│   └── run_tests.sh            # 编译 + 测试 + 运行 c_example
├── .github/workflows/
│   └── ci.yml                  # CI:Docker 内执行 run_tests.sh
├── crates/
│   ├── ffi_lib/                # 被 C 调用
│   │   ├── src/lib.rs
│   │   └── include/ffi_lib.h
│   ├── ffi_caller/             # Rust 调用 C
│   │   ├── build.rs
│   │   ├── src/main.rs
│   │   └── c_src/
│   └── asm_demo/               # 内联汇编(x86_64)
│       └── src/lib.rs
└── c_example/                  # C 程序,链接 ffi_lib
    ├── main.c
    └── Makefile

模块与依赖关系

  • Workspace(根目录 Cargo.toml)仅包含三个 crate:ffi_libffi_callerasm_demo。三者之间无 Cargo 依赖,各自独立。
  • ffi_lib:被 C 侧调用。仅依赖 libc。产出 libffi_lib.so/.dyliblibffi_lib.a,供 C 程序链接。
  • ffi_caller:Rust 调用 C。无其它 crate 依赖;通过 build.rs 编译并链接本 crate 下的 C 源码(c_src/*.c),不依赖 ffi_lib
  • asm_demo:内联汇编示例。无依赖,独立库。
  • c_example非 Cargo 成员,为独立 C 工程。构建时先执行 cargo build -p ffi_lib,再使用 gcc -lffi_lib 链接 target/debug 中的 libffi_lib,因此仅依赖 ffi_lib 的编译产物,与 ffi_callerasm_demo 无关系。
c_example (C)  ──链接──►  ffi_lib (Rust cdylib/staticlib)
                              │
ffi_caller (Rust)  ──build.rs 编译并链接──►  c_src/*.c(本 crate 内 C)
asm_demo (Rust)    (无外部依赖)

场景说明

1. C 调用 Rust(ffi_lib + c_example)

  • ffi_libcdylib/staticlib,导出 addconcat_strings/free_rust_stringPoint/distanceapply_callbackrust_helloinit_rust_library。使用 #[no_mangle]extern "C"#[repr(C)],字符串遵循“谁分配谁释放”。
  • include/ffi_lib.h:与上述 API 一致的 C 声明。
  • c_example:C 程序包含 ffi_lib.h,链接 libffi_lib,调用上述函数。编译产物为 c_example_binary

2. Rust 调用 C(ffi_caller)

  • build.rs:用 cc 编译 c_src/math.cc_src/string.cinclude("c_src")
  • c_src:极简 C 实现(c_addc_scale_arrayc_duplicate_string/c_free_string)。
  • main.rsextern "C" 声明并调用,带简单安全包装与单元测试。

3. 内联汇编(asm_demo)

  • x86_64 使用 asm!asm_addbswap_u64);其他架构为纯 Rust 实现。
  • 单元测试断言返回值,在 x86_64 下会执行真实汇编。

本地运行

# 进入项目根
cd rust_c_ffi_sample

# 运行全部测试与 c_example
./scripts/run_tests.sh

脚本依次执行:cargo test --workspacecargo build -p ffi_libcd c_example && make && ./c_example_binary

仅跑 Rust 测试:

cargo test --workspace

仅构建并运行 C 示例:

cargo build -p ffi_lib
cd c_example && make run

可执行文件名为 c_example_binary(由 c_example/Makefile 生成)。

Docker

在 x86_64 环境下构建并运行测试(内联汇编会真实执行):

docker build --platform linux/amd64 -t rust-ffi .
docker run --rm rust-ffi

容器内默认执行 ./scripts/run_tests.sh

自动化测试与 CI

  • scripts/run_tests.sh:顺序执行 workspace 测试、构建 ffi_lib、在 c_examplemake 并运行 ./c_example_binary;任一失败则退出。
  • GitHub Actions.github/workflows/ci.yml):在 push/pull_requestmainmaster 时,构建上述 Docker 镜像(--platform linux/amd64),并在容器内执行 ./scripts/run_tests.sh

文档

  • docs/ANALYZE.md:技术分析文档,详细说明项目中用到的 ABI、类型对应、字符串/结构体/回调约定、build.rs 与链接流程、内联汇编操作数与条件编译等。

参考

更多 ABI、类型对应与细节可参考项目配套草稿文档(若有 draft.txt 等)。