С 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() возвращает Pin<Box<Self>> вместо Self. Это приводит к выделению памяти в куче, потому что Pin<Ptr> должен работать с типом указателя, таким как Box.
В ::new() мы используем Pin::get_unchecked_mut(), чтобы получить изменяемую ссылку на буфер после того, как он закреплён. Это unsafe, потому что мы на время нарушаем гарантию pinning, чтобы инициализировать cursor. После этого момента нельзя перемещать SelfReferentialBuffer. Контракт безопасности Pin таков: как только значение закреплено, его адрес в памяти фиксирован до уничтожения.