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 されるまでそのメモリ位置は固定されます。