このページの目次
裸のポインタと FFI
*const Tと*mut Tは借用チェッカーの制約を受けません——任意のエイリアシングが可能で、有効なメモリを指していないこともあります。これらが存在する唯一の理由は FFI です:C ライブラリの呼び出し、C コールバックの受信、ハードウェアレジスタの操作。unsafeブロックは「安全チェックを無効にする」ものではなく、安全責任をコンパイラからプログラマへ移譲するものです。
裸のポインタ: borrow checker がないポインタ
let x = 42;
let ptr: *const i32 = &x; // 参照 → 裸のポインタ (安全)
unsafe
裸のポインタは borrow checker のルールに従う必要はありません——複数の *mut T が同じデータを指すことも可能で、null であることもあり、アライメントが保証されている必要もありません。すべての責任はプログラマに移譲されます。
C の int * との違い:Rust は *const T と *mut T を区別します(C の const int * と int * に類似)。ただし、C では const を暗黙的に破棄できますが、Rust ではできません。
extern "C": C 関数インターフェース
extern "C"
// Rust の名前マングリングを禁止
pub extern "C"
extern "C" は C の呼び出し規約を使用します:引数はターゲットプラットフォームの C ABI に従ってレジスタまたはスタック上に配置されます。x86-64 ではこれは System V AMD64 ABI です(rdi, rsi, rdx, rcx, r8, r9 による引数渡し)。extern "system"(Windows API)や extern "Rust"(デフォルト)も使用できます。
#[repr(C)]: C 互換のメモリレイアウト
// サイズ: 16 バイト
#[repr(C)] を指定しない場合、Rust コンパイラは構造体のサイズを最適化するためにフィールドの順序を並べ替えることができます。#[repr(C)] を追加すると、フィールドは宣言順に配置され、C ABI の要件に従ってパディングが挿入され、C の構造体とのバイナリ互換性が保証されます。#[repr(C, packed)] を使用するとすべてのパディングが削除されます(ただし、未アラインの参照が発生する可能性があるため unsafe です)。
bindgen: Rust FFI バインディングの自動生成
C のヘッダファイルから Rust の extern 宣言、構造体定義、定数を生成します:
// build.rs:
// src/lib.rs:
include!;
コールバック: C から Rust への呼び出し時の注意点
extern "C"
unsafe
Rust 関数をコールバックとして C に渡す場合、パニックは FFI 境界を越えてはなりません——コールバック内でパニックが発生し、アンワインドが C のスタックフレームを通過すると、未定義動作(UB)になります(C のスタックにはアンワインドテーブルがないため)。解決策として、コールバック内で std::panic::catch_unwind を使用してパニックをキャッチするか、Cargo.toml で panic = "abort" を設定します。
参考
- Rustonomicon: FFI, C ABI
- bindgen: rust-lang.github.io/rust-bindgen
- The Embedded Rust Book: FFI and C interaction
*Keywords: raw pointer, *const, mut, extern "C", #[no_mangle], repr(C), bindgen, FFI callback, C ABI