diff --git a/src/lib.rs b/src/lib.rs index 09c7d10..8ccc713 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,23 @@ pub use generic_array::ArrayLength; use generic_array::typenum::Unsigned; +mod enforcement_style { + /// A type-level choice between KeysAreValid and MayPanic + pub trait EnforcementStyle {} +} + +/// Type indicator for Slots whose keys can only be used to perform non-panicking access +pub struct KeysAreValid; + +impl enforcement_style::EnforcementStyle for KeysAreValid {} + +/// Type indicator for Slots whose keys may become unusable (or become unusable and later refer to +/// any other item allocated in the same slot), but which can delete items based on indices in +/// return. +pub struct MayPanic; + +impl enforcement_style::EnforcementStyle for MayPanic {} + pub struct Key { index: usize, _item_marker: PhantomData, @@ -91,24 +108,30 @@ use entry::Entry; // Data type that stores values and returns a key that can be used to manipulate // the stored values. // Values can be read by anyone but can only be modified using the key. -pub struct Slots - where N: ArrayLength> + Unsigned { +pub struct Slots + where N: ArrayLength> + Unsigned, + E: enforcement_style::EnforcementStyle, +{ items: GenericArray, N>, // Could be optimized by making it just usize and relying on free_count to determine its // validity next_free: Option, - free_count: usize + free_count: usize, + enforcement_style: PhantomData } -impl Slots - where N: ArrayLength> + Unsigned { +impl Slots + where N: ArrayLength> + Unsigned, + E: enforcement_style::EnforcementStyle +{ pub fn new() -> Self { let size = N::to_usize(); Self { items: GenericArray::generate(|i| i.checked_sub(1).map(Entry::EmptyNext).unwrap_or(Entry::EmptyLast)), next_free: size.checked_sub(1), - free_count: size + free_count: size, + enforcement_style: PhantomData, } } @@ -180,3 +203,21 @@ impl Slots } } } + +impl Slots + where N: ArrayLength> + Unsigned, +{ + + pub fn try_take(&mut self, key: usize) -> Option { + if self.try_read(key, |_| ()).is_none() { + return None; + } + + let taken = core::mem::replace(&mut self.items[key], Entry::EmptyLast); + self.free(key); + match taken { + Entry::Used(item) => Some(item), + _ => panic!() + } + } +} diff --git a/tests/test.rs b/tests/test.rs index 4a40f44..e0bdfe2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -9,6 +9,17 @@ fn key_can_be_used_to_read_value() { assert_eq!(5, slots.read(&k1, |&w| w)); } +#[test] +#[should_panic(expected="explicit panic")] +fn uncheckedindex_can_be_used_to_panic() { + let mut slots: Slots<_, U8, slots::MayPanic> = Slots::new(); + let k1 = slots.store(5).unwrap(); + let k2 = k1.index(); + + assert_eq!(Some(5), slots.try_take(k2)); + slots.take(k1); +} + #[test] fn size_can_be_1() { let mut slots: Slots<_, U1> = Slots::new();