すごいぞbindgen
rust-bindgenというツールを使うとC言語の.hファイルからバインディングを自動生成してくれます!すごく便利ですね!
今回はbindgenで変換した後のお話です。私がCに疎いので間違えた解釈をしていることもあるかもしれませんが、C向けAPIでよくあるパターンとその対処法を書いていきます。
NULLポインタを用意する
let nullpo: *mut std::ffi::c_void = std::ptr::null_mut();
ハンドル生成でお世話になる。ポインタはas
でキャストすることもできるみたいです。
目的データのポインタからデータをコピーする
let mut buf = vec![0; length];
let p_buf = buf.as_mut_ptr();
std::ptr::copy_nonoverlapping(p_src, p_buf, length);
Cのmemcpyと一緒。引数の順番は違う。セグフォの原因だいたいこいつ
コピー元がCの世界出身であれば正しくメモリ開放しましょう。
大きな構造体のメモリを初期化せずに確保する
let mut some_big_struct: MaybeUninit<SomeBigStruct> = MaybeUninit::uninit();
//ポインタを得る。
let p_some_big_struct = some_big_struct.as_mut_ptr();
初期化にかかる時間すらもったいない人向けです。
MaybeUninit
は未初期化の変数にアクセスできないように包んでくれます。
Cstringを作る
let cstr = CString::new("string").unwrap();
let p_cstr = cstr.as_ptr();
これに限らず、as_ptr系のメソッドを使うときはそれが指す領域が存在していないとセグフォします。
ワンライナー癖のある人はポインタだけ生成しないように注意!
参考:
ライブラリをリンクする
fn main(){println!(r"cargo:rustc-link-lib=dylib=path/to/library")}
ビルドスクリプトを書きます。
パスに.lib
などの拡張子は必要ない。(多分.dllだったり.soだったりの拡張子を無視して扱えるすごいシステム)
共用体を使う
#[repr(C)]
#[derive(Copy, Clone)]
union MyUnion {
f1: u32,
f2: f32,
}
そもそも共用体の中身はCopy可能でなけらばならないのでCopyトレイトはつけるだけお得。
存在自体がtransmute系のunsafeなので可能な限り使いたくない。
おわりに
他にもあったら追加します。