Programmingシステム開発者

Rustにおけるsystem FFI(Foreign Function Interface)の働きを説明してください。Cの関数を安全に呼び出すための要件や落とし穴は何ですか?

Hintsage AIアシスタントで面接を突破

回答

RustのFFIは、外部ライブラリ(例えばC/C++)で宣言された関数を呼び出し、Rustの関数を外部にエクスポートすることを可能にします。これにはexternキーワードが使用されます。要件:

  • すべてのFFI呼び出しはunsafeで宣言されている必要があります;
  • ABI(Application Binary Interface)が一致している必要があります(通常は「C」);
  • データ型が互換性がある必要があります — 固定サイズのプリミティブ(i32u64など)を使用することが重要です;
  • メモリ管理 — 所有権、メモリリーク、ダブルフリーに非常に注意することが必要です。

ラッパーの例:

extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("{}", abs(-5)); } }

RustからCで使用するために関数をエクスポートするには、次のようにします:

#[no_mangle] pub extern "C" fn sum(a: i32, b: i32) -> i32 { a + b }

トリッキーな質問

質問: Rustでは、C関数の呼び出しをunsafeでラップすると、スレッドセーフ性やUBの検出に関して正しく動作することが保証されますか?

回答: いいえ! unsafeは、あなた自身がすべての安全性要件(エイリアス、スレッドセーフ、メモリアクセス)を保証することのコンパイラーへの約束です。RustはCのコード内部でのチェックを行いません。例えば、ライブラリのコードにレースコンディションやUBがあると、Rustプログラムのランタイムが「壊れる」可能性があります。Rustコードがコンパイルされても、実際の実行はクラッシュを引き起こす可能性があります。


ストーリー

金融データプロバイダーで、チームはCライブラリをFFIを通じて統合しましたが、データ型のサイズを確認しませんでした。x86_64ではlongi64が一致すると思われましたが、他のプラットフォームではサイズが一致せず、間違ったメモリ読み取りが計算のバグを引き起こしました。

ストーリー

開発者はC APIのラッパーを作成しましたが、バッファへのポインタを渡すことを忘れました。Rustは自動的にメモリを解放するため、C関数は時折既に解放されたメモリを操作し、クラッシュやエラーを引き起こしました。

ストーリー

クライアント-サーバ製品で、RustモジュールがCライブラリにアクセスしましたが、マルチスレッド安全性を考慮していませんでした。このライブラリはスレッドセーフではなく、Rustプログラムは異なるスレッドから同時にアクセスし、それがクラッシュやデータ破損につながりました。