---
title: 裸のポインタと FFI
url: https://doc.liz6.com/ja/rust/07-unsafe-rust/01-raw-pointers-and-ffi
locale: ja
area: rust
tags:
- rust
- unsafe-rust
date: 2026-06-30
modified: 2026-07-16
description: '`const T` と `mut T` は借用チェッカーの制約を受けません——任意のエイリアシングが可能で、有効なメモリを指していないこともあります。これらが存在する唯一の理由は FFI です：C ライブラリの呼び出し、C コールバックの受信、ハードウェアレジスタの操作。`unsafe` ブロックは「安全チェックを無効にする」ものではなく、安全責任をコンパイラからプログラマへ移譲するものです。'
---

# 裸のポインタと FFI

> `*const T` と `*mut T` は借用チェッカーの制約を受けません——任意のエイリアシングが可能で、有効なメモリを指していないこともあります。これらが存在する唯一の理由は FFI です：C ライブラリの呼び出し、C コールバックの受信、ハードウェアレジスタの操作。`unsafe` ブロックは「安全チェックを無効にする」ものではなく、安全責任をコンパイラからプログラマへ移譲するものです。

## 裸のポインタ: borrow checker がないポインタ

```rust
let x = 42;
let ptr: *const i32 = &x;            // 参照 → 裸のポインタ (安全)
unsafe {
    println!("{}", *ptr);            // 間接参照 (unsafe — コンパイラは有効性を検証しない)
}
```

裸のポインタは borrow checker のルールに従う必要はありません——複数の `*mut T` が同じデータを指すことも可能で、null であることもあり、アライメントが保証されている必要もありません。すべての責任はプログラマに移譲されます。

C の `int *` との違い：Rust は `*const T` と `*mut T` を区別します（C の `const int *` と `int *` に類似）。ただし、C では `const` を暗黙的に破棄できますが、Rust ではできません。

## extern "C": C 関数インターフェース

```rust
extern "C" {
    fn abs(input: i32) -> i32;       // C 関数の宣言
}

#[no_mangle]                         // Rust の名前マングリングを禁止
pub extern "C" fn rust_callback(x: i32) -> i32 {
    x + 1                            // 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 互換のメモリレイアウト

```rust
#[repr(C)]
struct MyStruct {
    a: i32,                          // オフセット 0
    // パディング: 4 バイト           // C では f64 を 8 バイト境界にアラインする必要がある
    b: f64,                          // オフセット 8
}                                    // サイズ: 16 バイト
```

`#[repr(C)]` を指定しない場合、Rust コンパイラは構造体のサイズを最適化するためにフィールドの順序を並べ替えることができます。`#[repr(C)]` を追加すると、フィールドは宣言順に配置され、C ABI の要件に従ってパディングが挿入され、C の構造体とのバイナリ互換性が保証されます。`#[repr(C, packed)]` を使用するとすべてのパディングが削除されます（ただし、未アラインの参照が発生する可能性があるため unsafe です）。

## bindgen: Rust FFI バインディングの自動生成

C のヘッダファイルから Rust の `extern` 宣言、構造体定義、定数を生成します：

```rust
// build.rs:
fn main() {
    println!("cargo:rerun-if-changed=wrapper.h");
    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .allowlist_function("my_lib_.*")   // これらの関数のみバインディングを生成
        .generate().expect("bindgen failed");
    bindings.write_to_file("src/bindings.rs").unwrap();
}
// src/lib.rs:
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
```

## コールバック: C から Rust への呼び出し時の注意点

```rust
extern "C" fn callback(x: i32) {
    println!("C called with {}", x);
}
unsafe { register_callback(callback as unsafe extern "C" fn(i32)); }
```

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*
