項目35:手動でFFIマッピングを書かずにbindgenを用いよう
https://effective-rust.com/bindgen.html
cargo-bindgen の必要性
FFI 境界 を超えて C の 構造体 や関数を利用する場合、対応する Rust の宣言が必要
この時 Rust と C のコードは整合する必要があるが、ツールチェーンではチェックできない
そのため、「cargo-bindgen を用いて、C のコードから Rust の宣言を生成しよう」
これにより、CI 上で生成して既存の生成コードと整合するかチェックできる(項目32:CIシステムを設定しよう)
e.g.
code:lib.h
/* File lib.h */
#include <stdint.h>
typedef struct {
uint8_t byte;
uint32_t integer;
} FfiStruct;
int add(int x, int y);
uint32_t add32(uint32_t x, uint32_t y);
code:sh
$ bindgen \
# メモリ配置が対応するバインディングと一致しているかのテストを生成しない
--no-layout-tests \
# Rust にバインディングする関数名(正規表現)
--allowlist-function="add.*" \
# Rust にバインディングする型名
--allowlist-type=FfiStruct \
-o src/generated.rs \
lib.h
code:src/generated.rs
/* automatically generated by rust-bindgen 0.71.1 */
#repr(C)
#derive(Debug, Copy, Clone)
pub struct FfiStruct {
pub byte: u8,
pub integer: u32,
}
unsafe extern "C" {
pub fn add(x: ::std::os::raw::c_int, y: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}
unsafe extern "C" {
pub fn add32(x: u32, y: u32) -> u32;
}
code:rs
include!("generated.rs");
bindgen の --allowlist-function や --allowlist-type などのオプションを用いると、C のライブラリを Rust で段階的に公開するアプローチが可能になる
e.g. xyzzy というライブラリの場合
xyzzy-sys クレート: bindgen が生成したコードだけを保持するクレート
このクレートの使用は必ず unsafe となる
xyzzy クレート: unsafe なコードを カプセル化 し、安全な Rust らしいアクセスを実現する
項目16:unsafeコードを書かないようにしよう
C++ の場合
bindgen は C++ のアイテムを一部扱うことができる が、制約が多い
そのため、「C++ と Rust のよりスムーズに連携させたい場合は、cxx クレートの使用を検討しよう」
それでも制限はあるが...
cxx では、Rust ↔︎ C++ の 双方の コードを自動生成するアプローチを取る
#Rust #Effective_Rust_―_Rustコードを改善し、エコシステムを最大限に活用するための35項目