#![no_std]
use core::{marker::PhantomPinned, pin::Pin};
use iter::{Iter, IterMut};
pub mod iter;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct PinArray<T, const SIZE: usize> {
elements: [T; SIZE],
_pin: PhantomPinned,
}
impl<T: Default, const SIZE: usize> Default for PinArray<T, SIZE> {
fn default() -> Self {
Self {
elements: core::array::from_fn(|_| Default::default()),
_pin: Default::default(),
}
}
}
impl<T, const SIZE: usize> PinArray<T, SIZE> {
pub fn new(elements: [T; SIZE]) -> Self {
Self {
elements,
_pin: PhantomPinned,
}
}
pub const fn len(&self) -> usize {
SIZE
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, idx: usize) -> Option<&T> {
self.elements.get(idx)
}
pub fn get_pin(self: Pin<&mut Self>, idx: usize) -> Option<Pin<&mut T>> {
unsafe { self.get_unchecked_mut() }
.elements
.get_mut(idx)
.map(|e| unsafe { Pin::new_unchecked(e) })
}
pub fn as_ref_array(&self) -> [&T; SIZE] {
core::array::from_fn(|i| &self.elements[i])
}
pub fn as_pin_array<'me>(self: Pin<&'me mut Self>) -> [Pin<&'me mut T>; SIZE] {
let arr = unsafe { self.get_unchecked_mut().elements.as_mut_ptr() };
core::array::from_fn(|i| {
let p = unsafe { arr.add(i) };
unsafe { Pin::new_unchecked(p.as_mut().unwrap()) }
})
}
pub fn iter(&self) -> Iter<'_, T, SIZE> {
Iter { i: 0, els: self }
}
pub fn iter_mut(self: Pin<&mut Self>) -> IterMut<'_, T, SIZE> {
IterMut::new(unsafe { self.get_unchecked_mut() })
}
}
impl<T: Unpin, const SIZE: usize> Unpin for PinArray<T, SIZE> {}
#[cfg(test)]
mod tests {
use core::{
marker::{PhantomData, PhantomPinned},
ops::Deref,
pin::{pin, Pin},
};
use crate::PinArray;
#[derive(Clone, Copy, Debug, Default, Eq)]
struct NotUnpin {
_p: PhantomPinned,
v: u8,
}
impl NotUnpin {
fn new(v: u8) -> Self {
Self {
_p: PhantomPinned,
v,
}
}
}
impl PartialEq for NotUnpin {
fn eq(&self, other: &Self) -> bool {
self.v == other.v
}
}
#[track_caller]
fn mut_iter_test<T: Copy + Eq + core::fmt::Debug, const SZ: usize>(mut els: [T; SZ]) {
let mut p = core::pin::pin!(PinArray::new(els));
let iter_p = p.as_mut().iter_mut();
let iter_els = els.iter_mut().map(|e| unsafe { Pin::new_unchecked(e) });
iter_p
.zip(iter_els)
.for_each(|(e_p, e_els)| assert_eq!(e_p, e_els));
}
#[test]
fn mut_iterator_multi_borrow_ub() {
mut_iter_test([1, 2, 3, 4]);
}
#[test]
fn mut_iter_needs_pin() {
mut_iter_test(core::array::from_fn::<_, 3, _>(|i| NotUnpin::new(i as u8)));
}
#[test]
fn mut_iter_zst() {
mut_iter_test([PhantomData::<()>, PhantomData, PhantomData]);
}
#[test]
fn as_pin_array_mut_ub() {
let arr = pin!(PinArray::new([1, 2, 3]));
let vs = arr.as_pin_array();
let v1 = vs[0].deref();
let v2 = vs[1].deref();
assert_ne!(v1, v2);
}
}
#[cfg(test)]
mod impl_tests {
use super::*;
use static_assertions::{assert_impl_all, assert_not_impl_all};
#[allow(unused)]
struct NotPin(PhantomPinned);
assert_impl_all!(PinArray<u32, 1>: Unpin);
assert_not_impl_all!(PinArray<NotPin, 1>: Unpin);
}