Pin<Ptr> を使う

Pinning を使うと、Rust プログラマーは C++ のクラスにはるかに近い型を作成できます。

// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0

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

/// 移動できない自己参照バッファ。
#[derive(Debug)]
pub struct SelfReferentialBuffer {
    data: [u8; 1024],
    cursor: *mut u8,
    _pin: PhantomPinned,
}

impl SelfReferentialBuffer {
    pub fn new() -> Pin<Box<Self>> {
        let buffer = SelfReferentialBuffer {
            data: [0; 1024],
            cursor: std::ptr::null_mut(),
            _pin: PhantomPinned,
        };
        let mut pinned = Box::pin(buffer);

        unsafe {
            let mut_ref = Pin::get_unchecked_mut(pinned.as_mut());
            mut_ref.cursor = mut_ref.data.as_mut_ptr();
        }

        pinned
    }

    pub fn read(&self, n_bytes: usize) -> &[u8] {
        unsafe {
            let start = self.data.as_ptr();
            let end = start.add(self.data.len());
            let cursor = self.cursor as *const u8;

            assert!((start..=end).contains(&cursor), "cursor is out of bounds");

            let offset = cursor.offset_from(start) as usize;
            let available = self.data.len().saturating_sub(offset);
            let len = n_bytes.min(available);

            &self.data[offset..offset + len]
        }
    }

    pub fn write(mut self: Pin<&mut Self>, bytes: &[u8]) {
        let this = unsafe { self.as_mut().get_unchecked_mut() };
        unsafe {
            let start = this.data.as_mut_ptr();
            let end = start.add(1024);

            assert!((start..=end).contains(&this.cursor), "cursor is out of bounds");
            let available = end.offset_from(this.cursor) as usize;
            let len = bytes.len().min(available);

            std::ptr::copy_nonoverlapping(bytes.as_ptr(), this.cursor, len);
            this.cursor = this.cursor.add(len);
        }
    }
}

関数シグネチャが変わっていることに注目してください。たとえば、::new()Self ではなく Pin<Box<Self>> を返します。これはヒープ割り当てを伴います。 Pin<Ptr>Box のようなポインタ型とともに動作する必要があるためです。

::new() では、Pin::get_unchecked_mut() を使って、ピン留めされた 後の バッファへの可変参照を取得しています。これは、cursor を初期化するために、 一時的に pinning の保証を破っているため unsafe です。この時点以降、 SelfReferentialBuffer を移動しないようにしなければなりません。Pin の安全性契約では、 いったん値がピン留めされると、drop されるまでそのメモリ位置は固定されます。