Rust 调 C 函数需要什么?字符串怎么传?结构体怎么传?回调怎么写?声明写错会怎样?这篇用真实的 libc 和一个自己编译的 C 静态库一次验证清楚。
编译:成功
运行(退出码 0):
strlen("héllo") = 6(C 数的是字节:é 占 2 字节) Rust 的 len() = 6
abs(-42) = 42 toupper('a') = A
getpid() == std::process::id(): true
CString::new("a\0b") = Err("nul byte found in provided data at position: 1")(字符串中间不能有 \0)
c"…" 字面量(以 \0 结尾的 &CStr):"字面量" 字节数 9可空指针的 FFI 表示见Option<&T> 的空指针优化;repr(C) 见#[repr(C)] 与内存布局。
编译:成功
运行(退出码 0):
qsort 用 Rust 写的 extern "C" 比较函数降序排序:[9, 8, 7, 5, 3, 2, 1];回调被 C 调用了 17 次 clang -c vec2.c → vec2.o(退出码 0);ar rcs libvec2.a(退出码 0)
rustc 链接静态库: 成功
vec2_add(按值传递、按值返回结构体) = Vec2 { x: 11.0, y: 22.0 }
vec2_len2(&r)(按指针传递)= 605
vec2_scale_all 原地修改 Rust 的数组:[Vec2 { x: 10.0, y: 10.0 }, Vec2 { x: 20.0, y: 30.0 }]
fill_greeting 往 Rust 的缓冲区写了 12 字节:"hello from C"
缓冲区太小(4 字节):C 返回 0(约定:0 表示放不下)
sum_i32(Rust 数组) = 15用 clang 与 ar 手工构建静态库,没有使用 cc crate 与 build.rs(见Build Script build.rs)。
right 编译通过(没有任何警告);运行退出码 0,输出 '6' ret_f64 编译通过(没有任何警告);运行退出码 0,输出 '0.0000000000…(很小的数,共 326 个字符)' no_n 编译通过(没有任何警告);运行退出码 -11,输出 '' n_wrong_ptr 编译通过(没有任何警告);运行退出码 0,输出 '288628513' 编译器没有机会核对 C 头文件:声明与定义是否一致完全靠人;bindgen / cbindgen 就是用来自动生成声明的(本文没有使用)
输出里的垃圾值每次运行可能不同。