Chapter 3: 自己参照構造体の深層理解

この章の目標

この章を読み終えると、以下のことが理解できるようになります:

  • 自己参照構造体がRustで困難な理由の本質
  • PinとUnpinの仕組みと実装
  • std::pinモジュールの内部構造
  • FutureとAsync/Awaitのライフタイム
  • ouroborosとrentalクレートの設計原理
  • なぜ自己参照構造体が重要か

    自己参照構造体は、以下のような実世界のユースケースで必要になります:

  • 非同期プログラミング: Future内部での状態管理
  • 高性能パーサー: 入力データへの参照を保持
  • ゼロコピーデシリアライゼーション: メモリコピーなしでデータ解析
  • イベントループ実装: コールバック関数の管理

この章で学ぶPinは、async/await構文の基盤技術です。

---

1. 自己参照構造体の問題

1.1 なぜ自己参照が難しいのか

単純な試み

struct SelfRef {
    data: String,
    pointer: &String,  // コンパイルエラー!
}

エラーメッセージ

error[E0106]: missing lifetime specifier
 --> src/main.rs:3:14
  |
3 |     pointer: &String,
  |              ^ expected named lifetime parameter

ライフタイムを追加しても...

struct SelfRef<'a> {
    data: String,
    pointer: &'a String,
}

impl<'a> SelfRef<'a> {
    fn new(text: String) -> Self {
        let data = text;
        SelfRef {
            data,
            pointer: &data,  // エラー!borrowed value does not live long enough
        }
    }
}

1.2 根本的な問題:ムーブセマンティクス

問題の本質

初期状態:
┌─────────────────┐
│ SelfRef         │
│ ┌─────────────┐ │
│ │ data: "hi" │ │
│ └─────────────┘ │
│        ↑        │
│ ┌──────┴──────┐ │
│ │ pointer     │ │
│ └─────────────┘ │
└─────────────────┘
Address: 0x1000

ムーブ後:
┌─────────────────┐
│ SelfRef         │
│ ┌─────────────┐ │
│ │ data: "hi" │ │  ← 新しいアドレス!
│ └─────────────┘ │
│        ↑        │
│ ┌──────┼──────┐ │
│ │ pointer: ───┼─┼──► 0x1000 (無効!)
│ └─────────────┘ │
└─────────────────┘
Address: 0x2000

視覚化

fn demonstrate_problem() {
    let mut s1 = SelfRef::new("hello");

    // s1をムーブ
    let s2 = s1;  // ← data のアドレスが変わる!
                  //   でも pointer は古いアドレスを指したまま

    // s2.pointer は今やダングリングポインタ!
}

1.3 Rustの所有権モデルとの衝突

Rustの3つの保証

  • ムーブセマンティクス: 値は自由に移動できる
  • メモリ安全性: ダングリングポインタは存在しない
  • ゼロコスト抽象化: 実行時オーバーヘッドなし
  • 自己参照の要求

  • データの物理アドレスを固定 ← ムーブと矛盾!
  • 解決策の方向性

  • 禁止する: ムーブを不可能にする → Pin
  • 更新する: ムーブ時にポインタを修正 → 実行時コスト
  • 間接参照: Boxやスマートポインタ → ヒープ割り当て

Rustは1番(Pinを選択しました。

---

2. Pinの仕組み

2.1 Pinの基本定義

std::pinモジュール

pub struct Pin<P> {
    pointer: P,
}

impl<P: Deref> Pin<P> {
    pub unsafe fn new_unchecked(pointer: P) -> Pin<P> {
        Pin { pointer }
    }

    pub fn as_ref(&self) -> Pin<&P::Target> {
        // 重要: Pinを維持したまま参照を取得
        unsafe { Pin::new_unchecked(&*self.pointer) }
    }
}

Pinの保証

Pin<P<T>> は以下を保証する:

1. T が !Unpin の場合:
   - T をムーブできない
   - &mut T を取得できない(ムーブに使えるため)

2. T が Unpin の場合:
   - 通常の型として扱える
   - Pin<P<T>> ≈ P<T>

2.2 Unpinトレイト

定義

pub auto trait Unpin {}

auto trait

  • ほとんどの型に自動実装される
  • 明示的にオプトアウト可能

Unpinな型(デフォルト):

struct Regular {
    x: i32,
    y: String,
}
// Regular は自動的に Unpin

fn move_regular(p: Pin<Box<Regular>>) {
    let r: Box<Regular> = Pin::into_inner(p);  // OK!
}

!Unpinな型(オプトアウト):

use std::marker::PhantomPinned;

struct NotUnpin {
    data: String,
    pointer: *const String,
    _pin: PhantomPinned,  // これで !Unpin になる
}

fn cannot_move(p: Pin<Box<NotUnpin>>) {
    // let n = Pin::into_inner(p);  // コンパイルエラー!
}

2.3 PhantomPinnedの役割

定義

pub struct PhantomPinned;

impl !Unpin for PhantomPinned {}

使用例

use std::marker::PhantomPinned;
use std::pin::Pin;

struct SelfRef {
    data: String,
    // 生ポインタを使う(ライフタイムの問題を回避)
    pointer: *const String,
    _pin: PhantomPinned,
}

impl SelfRef {
    fn new(text: String) -> Pin<Box<Self>> {
        let mut boxed = Box::pin(SelfRef {
            data: text,
            pointer: std::ptr::null(),
            _pin: PhantomPinned,
        });

        // 安全にポインタを設定
        let self_ptr: *const String = &boxed.data;
        unsafe {
            let mut_ref = Pin::as_mut(&mut boxed);
            Pin::get_unchecked_mut(mut_ref).pointer = self_ptr;
        }

        boxed
    }

    fn get_data(self: Pin<&Self>) -> &str {
        &self.data
    }

    fn get_pointer_data(self: Pin<&Self>) -> &str {
        unsafe {
            // pointer は data を指している
            &*self.pointer
        }
    }
}

メモリレイアウト

Box<SelfRef> in heap:
┌─────────────────────────┐
│ SelfRef                 │
│ ┌─────────────────────┐ │
│ │ data: String        │ │ ← 0x7fff1000
│ │   ptr: 0xabcd      │ │
│ │   len: 5           │ │
│ │   cap: 5           │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ pointer: 0x7fff1000│ │ ← data を指す
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ _pin: PhantomPinned│ │ ← ゼロサイズ
│ └─────────────────────┘ │
└─────────────────────────┘

Pin<Box<SelfRef>> により、この Box はムーブ不可

---

3. std::pinモジュールの内部構造

3.1 主要なAPI

安全なAPI

// 1. Pin::new - Unpin型のみ
impl<P: Deref<Target: Unpin>> Pin<P> {
    pub fn new(pointer: P) -> Pin<P> {
        unsafe { Pin::new_unchecked(pointer) }
    }
}

// 2. into_inner - Unpin型のみ
impl<P: Deref<Target: Unpin>> Pin<P> {
    pub fn into_inner(pin: Pin<P>) -> P {
        pin.pointer
    }
}

// 3. get_mut - Unpin型のみ
impl<P: DerefMut<Target: Unpin>> Pin<P> {
    pub fn get_mut(self: Pin<&mut P>) -> &mut P::Target {
        unsafe { Pin::get_unchecked_mut(self) }
    }
}

unsafe API

// 4. new_unchecked - 任意の型
impl<P> Pin<P> {
    pub unsafe fn new_unchecked(pointer: P) -> Pin<P> {
        Pin { pointer }
    }
}

// 5. get_unchecked_mut - !Unpin型でも可
impl<P: DerefMut> Pin<P> {
    pub unsafe fn get_unchecked_mut(self: Pin<&mut P>) -> &mut P::Target {
        &mut *self.pointer
    }
}

// 6. map_unchecked_mut - フィールド投影
impl<P: DerefMut> Pin<P> {
    pub unsafe fn map_unchecked_mut<U, F>(
        self: Pin<&mut P>,
        func: F,
    ) -> Pin<&mut U>
    where
        F: FnOnce(&mut P::Target) -> &mut U,
    {
        let pointer = Pin::get_unchecked_mut(self);
        Pin::new_unchecked(func(pointer))
    }
}

3.2 構造的Pinning vs 非構造的Pinning

構造的Pinning

  • 構造体がPinnedなら、フィールドもPinned
  • #[pin]属性で管理(pin-projectクレート)

非構造的Pinning

  • 一部のフィールドのみPinned
  • 他のフィールドは自由にムーブ可能

use std::pin::Pin;
use std::marker::PhantomPinned;

struct Mixed {
    // 構造的ピン: この参照は固定
    pinned_data: String,
    self_ref: *const String,

    // 非構造的: 自由にムーブ可能
    movable_data: Vec<i32>,

    _pin: PhantomPinned,
}

impl Mixed {
    fn get_movable_mut(self: Pin<&mut Self>) -> &mut Vec<i32> {
        // movable_data は Unpin なので安全に取得
        unsafe {
            &mut self.get_unchecked_mut().movable_data
        }
    }
}

3.3 pin-projectクレートの原理

手動実装(複雑)

struct MyStruct {
    pinned: String,
    unpinned: Vec<i32>,
    _pin: PhantomPinned,
}

impl MyStruct {
    fn project(self: Pin<&mut Self>) -> (Pin<&mut String>, &mut Vec<i32>) {
        unsafe {
            let this = self.get_unchecked_mut();
            (
                Pin::new_unchecked(&mut this.pinned),
                &mut this.unpinned,
            )
        }
    }
}

pin-project使用(簡潔)

use pin_project::pin_project;

#[pin_project]
struct MyStruct {
    #[pin]
    pinned: String,
    unpinned: Vec<i32>,
}

// 自動生成された project() メソッドを使用
fn use_projection(mut s: Pin<&mut MyStruct>) {
    let projected = s.as_mut().project();
    // projected.pinned: Pin<&mut String>
    // projected.unpinned: &mut Vec<i32>
}

---

4. FutureとAsync/Awaitのライフタイム

4.1 Future Traitの定義

std::future::Future

pub trait Future {
    type Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
    //      ^^^^^^^^^^^^^^^^
    //      Pin<&mut Self> が必須!
}

pub enum Poll<T> {
    Ready(T),
    Pending,
}

なぜPinが必要か

async fn example() {
    let x = String::from("hello");
    let y = &x;  // ← x への参照を保持

    other_async().await;  // ← ここで中断・再開

    println!("{}", y);  // ← y が有効でなければならない
}

内部構造(概念)

// async fn は State Machine に変換される
enum ExampleFuture {
    Start,
    AwaitingOther {
        x: String,
        y: *const String,  // ← 自己参照!
    },
    Done,
}

impl Future for ExampleFuture {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        // Futureがムーブされたら y がダングリングポインタになる
        // だから Pin<&mut Self> が必要
        // ...
    }
}

4.2 Async関数のライフタイム

基本的なasync関数

async fn read_file(path: &str) -> String {
    // path はこの関数のライフタイム内で有効
    tokio::fs::read_to_string(path).await.unwrap()
}

展開後(概念)

fn read_file<'a>(path: &'a str) -> impl Future<Output = String> + 'a {
    //                                                             ^^^
    //                                     Future は 'a より長生きできない
    async move {
        tokio::fs::read_to_string(path).await.unwrap()
    }
}

複雑な例

async fn process<'a>(
    data: &'a [u8],
    config: &'a Config,
) -> Result<Output<'a>, Error> {
    let mut buffer = Vec::new();

    // 非同期処理
    let result = parse_async(data).await?;

    // 'a を含む結果を返す
    Ok(Output {
        parsed: result,
        original: data,  // ← 'a への参照
        cfg: config,     // ← 'a への参照
    })
}

struct Output<'a> {
    parsed: ParsedData,
    original: &'a [u8],
    cfg: &'a Config,
}

4.3 Futureの組み合わせとライフタイム

join!マクロ

use tokio::join;

async fn parallel_tasks<'a>(
    db: &'a Database,
) -> (Result<User, Error>, Result<Posts, Error>) {
    join!(
        db.get_user(),
        db.get_posts(),
    )
    // 両方のFutureが 'a を共有
}

select!マクロ

use tokio::select;

async fn race_tasks<'a>(
    source1: &'a Source,
    source2: &'a Source,
) -> Data {
    select! {
        data = source1.fetch() => data,
        data = source2.fetch() => data,
        // どちらか早い方
    }
}

ライフタイム図

┌─────────────────────────────────────┐
│ async fn のライフタイム 'a          │
│                                     │
│  ┌────────────────┐                │
│  │ Future 1       │                │
│  │ holds &'a Data │                │
│  └────────────────┘                │
│         ∧                           │
│         │ join!/select!            │
│         ∨                           │
│  ┌────────────────┐                │
│  │ Future 2       │                │
│  │ holds &'a Data │                │
│  └────────────────┘                │
│                                     │
└─────────────────────────────────────┘

---

5. ouroborosとrentalクレートの分析

5.1 rentalクレート(非推奨)

コンセプト

  • 自己参照構造体のコード生成
  • Rust 1.0時代の解決策
  • 現在は非推奨(Pinの登場により)

// rental crate(古い方法)
use rental::rental;

rental! {
    pub mod rent_string {
        use super::*;

        #[rental]
        pub struct OwnedStr {
            buffer: String,
            slice: &'buffer str,  // buffer を参照
        }
    }
}

問題点

  • マクロが複雑
  • デバッグが困難
  • 内部でunsafeを多用

5.2 ouroborosクレート(現代的)

設計思想

  • Pinベース
  • proc_macroによるコード生成
  • 安全性を最大化

基本使用例

use ouroboros::self_referencing;

#[self_referencing]
struct MyStruct {
    data: String,

    #[borrows(data)]
    #[covariant]
    slice: &'this str,
}

fn main() {
    let my_struct = MyStructBuilder {
        data: "Hello, World!".to_string(),
        slice_builder: |data| &data[0..5],
    }.build();

    my_struct.with_slice(|slice| {
        println!("{}", slice);  // "Hello"
    });
}

メモリレイアウト

MyStruct(ヒープ上):
┌──────────────────────────┐
│ data: String             │
│ ┌──────────────────────┐ │ ← 0x1000
│ │ ptr: heap → "Hello" │ │
│ │ len: 13              │ │
│ │ cap: 13              │ │
│ └──────────────────────┘ │
│                          │
│ slice: &str              │
│ ┌──────────────────────┐ │
│ │ ptr: 0x1000 (data)  │ │ ← data を指す!
│ │ len: 5               │ │
│ └──────────────────────┘ │
└──────────────────────────┘

Pin により、この構造体はムーブ不可

5.3 高度な使用例

複数の自己参照

#[self_referencing]
struct Parser {
    input: String,

    #[borrows(input)]
    tokens: Vec<&'this str>,

    #[borrows(tokens)]
    ast: Vec<&'this &'this str>,  // tokens 内の要素への参照
}

可変参照

#[self_referencing]
struct MutableStruct {
    data: Vec<i32>,

    #[borrows(mut data)]
    slice: &'this mut [i32],
}

impl MutableStruct {
    fn modify(&mut self) {
        self.with_slice_mut(|slice| {
            slice[0] = 42;
        });
    }
}

5.4 ouroboros vs Pin手動実装

ouroboros

// 簡潔
#[self_referencing]
struct Easy {
    data: String,
    #[borrows(data)]
    slice: &'this str,
}

手動Pin実装

// 複雑だが柔軟
use std::pin::Pin;
use std::marker::PhantomPinned;

struct Manual {
    data: String,
    slice: *const str,
    _pin: PhantomPinned,
}

impl Manual {
    fn new(s: String) -> Pin<Box<Self>> {
        let mut b = Box::pin(Manual {
            data: s,
            slice: std::ptr::null(),
            _pin: PhantomPinned,
        });

        let slice_ptr: *const str = &b.data;
        unsafe {
            let mut_ref = Pin::as_mut(&mut b);
            Pin::get_unchecked_mut(mut_ref).slice = slice_ptr;
        }

        b
    }

    fn get_slice(self: Pin<&Self>) -> &str {
        unsafe { &*self.slice }
    }
}

---

6. 実践的なパターン

6.1 Async Runtimeの内部

Tokio Futureの簡略化されたモデル

struct JoinHandle<T> {
    // Task の内部状態(Pinned Future)
    future: Pin<Box<dyn Future<Output = T> + Send>>,

    // 完了通知
    waker: Arc<Mutex<Option<Waker>>>,
}

impl<T> Future for JoinHandle<T> {
    type Output = T;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
        // future は Pinned されている
        let future = self.future.as_mut();
        future.poll(cx)
    }
}

6.2 ゼロコピーパーサー

設計

use ouroboros::self_referencing;

#[self_referencing]
struct ZeroCopyParser {
    // 入力データ(所有)
    input: Vec<u8>,

    // 入力データへの参照(パース結果)
    #[borrows(input)]
    parsed: ParsedData<'this>,
}

struct ParsedData<'a> {
    header: &'a [u8],
    body: &'a [u8],
    footer: &'a [u8],
}

impl ZeroCopyParser {
    fn parse(data: Vec<u8>) -> Self {
        ZeroCopyParserBuilder {
            input: data,
            parsed_builder: |input| {
                // ゼロコピーでパース
                let header_end = find_header_end(input);
                let body_end = find_body_end(input);

                ParsedData {
                    header: &input[0..header_end],
                    body: &input[header_end..body_end],
                    footer: &input[body_end..],
                }
            },
        }.build()
    }
}

fn find_header_end(data: &[u8]) -> usize {
    // 実装省略
    0
}

fn find_body_end(data: &[u8]) -> usize {
    // 実装省略
    data.len()
}

6.3 イベント駆動システム

コールバックとライフタイム

use std::pin::Pin;
use std::marker::PhantomPinned;

struct EventLoop {
    events: Vec<Event>,

    // 自己参照: callbacks は events を参照
    callbacks: Vec<*const Event>,

    _pin: PhantomPinned,
}

struct Event {
    id: u64,
    data: String,
}

impl EventLoop {
    fn new() -> Pin<Box<Self>> {
        Box::pin(EventLoop {
            events: Vec::new(),
            callbacks: Vec::new(),
            _pin: PhantomPinned,
        })
    }

    fn add_event(mut self: Pin<&mut Self>, event: Event) {
        unsafe {
            let this = self.as_mut().get_unchecked_mut();
            this.events.push(event);

            // 最後のイベントへのポインタ
            let event_ptr = this.events.last().unwrap() as *const Event;
            this.callbacks.push(event_ptr);
        }
    }

    fn process(self: Pin<&Self>) {
        for callback_ptr in &self.callbacks {
            unsafe {
                let event = &**callback_ptr;
                println!("Processing event {}: {}", event.id, event.data);
            }
        }
    }
}

---

まとめ

重要なポイント

  • 自己参照構造体の本質
- Rustのムーブセマンティクスと衝突 - Pinで解決

  • Pinの仕組み
- Unpin: 通常の型 - !Unpin: ムーブ不可の型 - PhantomPinned で制御

  • Async/Await
- Futureは自己参照構造体 - poll()メソッドは Pin<&mut Self>

  • 実用的なツール
- ouroboros: 安全な自己参照 - pin-project: フィールド投影

次のステップ

次章では、以下を学習します:

  • Generic Associated Types(GATs)
  • Lending Iterators
  • Streaming Iterators
  • 高度なAsync Lifetimeパターン

自己参照構造体の理解は、Rustの最も高度なメモリ安全性機構を理解する鍵です。