Skip to content

Commit 10f870e

Browse files
committed
Impl entry-API for la_arena::ArenaMap
We enforce integral and `Copy` key, so some key-related functions are not necessary since user can just reuse the index for the `entry` call.
1 parent c203ac2 commit 10f870e

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

lib/la-arena/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::{
1212
};
1313

1414
mod map;
15-
pub use map::ArenaMap;
15+
pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry};
1616

1717
/// The raw index of a value in an arena.
1818
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

lib/la-arena/src/map.rs

+107
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ impl<T, V> ArenaMap<Idx<T>, V> {
5656
self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
5757
}
5858

59+
/// Gets the given key's corresponding entry in the map for in-place manipulation.
60+
pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
61+
let idx = Self::to_idx(idx);
62+
self.v.resize_with((idx + 1).max(self.v.len()), || None);
63+
match &mut self.v[idx] {
64+
slot @ Some(_) => Entry::Occupied(OccupiedEntry { slot, _ty: PhantomData }),
65+
slot @ None => Entry::Vacant(VacantEntry { slot, _ty: PhantomData }),
66+
}
67+
}
68+
5969
fn to_idx(idx: Idx<T>) -> usize {
6070
u32::from(idx.into_raw()) as usize
6171
}
@@ -83,3 +93,100 @@ impl<T, V> Default for ArenaMap<Idx<V>, T> {
8393
Self::new()
8494
}
8595
}
96+
97+
/// A view into a single entry in a map, which may either be vacant or occupied.
98+
///
99+
/// This `enum` is constructed from the [`entry`] method on [`ArenaMap`].
100+
///
101+
/// [`entry`]: ArenaMap::entry
102+
pub enum Entry<'a, IDX, V> {
103+
/// A vacant entry.
104+
Vacant(VacantEntry<'a, IDX, V>),
105+
/// An occupied entry.
106+
Occupied(OccupiedEntry<'a, IDX, V>),
107+
}
108+
109+
impl<'a, IDX, V> Entry<'a, IDX, V> {
110+
/// Ensures a value is in the entry by inserting the default if empty, and returns a mutable reference to
111+
/// the value in the entry.
112+
pub fn or_insert(self, default: V) -> &'a mut V {
113+
match self {
114+
Self::Vacant(ent) => ent.insert(default),
115+
Self::Occupied(ent) => ent.into_mut(),
116+
}
117+
}
118+
119+
/// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
120+
/// a mutable reference to the value in the entry.
121+
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
122+
match self {
123+
Self::Vacant(ent) => ent.insert(default()),
124+
Self::Occupied(ent) => ent.into_mut(),
125+
}
126+
}
127+
128+
/// Provides in-place mutable access to an occupied entry before any potential inserts into the map.
129+
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Self {
130+
if let Self::Occupied(ent) = &mut self {
131+
f(ent.get_mut());
132+
}
133+
self
134+
}
135+
}
136+
137+
impl<'a, IDX, V> Entry<'a, IDX, V>
138+
where
139+
V: Default,
140+
{
141+
/// Ensures a value is in the entry by inserting the default value if empty, and returns a mutable reference
142+
/// to the value in the entry.
143+
pub fn or_default(self) -> &'a mut V {
144+
self.or_insert_with(Default::default)
145+
}
146+
}
147+
148+
/// A view into an vacant entry in a [`ArenaMap`]. It is part of the [`Entry`] enum.
149+
pub struct VacantEntry<'a, IDX, V> {
150+
slot: &'a mut Option<V>,
151+
_ty: PhantomData<IDX>,
152+
}
153+
154+
impl<'a, IDX, V> VacantEntry<'a, IDX, V> {
155+
/// Sets the value of the entry with the `VacantEntry`’s key, and returns a mutable reference to it.
156+
pub fn insert(self, value: V) -> &'a mut V {
157+
self.slot.insert(value)
158+
}
159+
}
160+
161+
/// A view into an occupied entry in a [`ArenaMap`]. It is part of the [`Entry`] enum.
162+
pub struct OccupiedEntry<'a, IDX, V> {
163+
slot: &'a mut Option<V>,
164+
_ty: PhantomData<IDX>,
165+
}
166+
167+
impl<'a, IDX, V> OccupiedEntry<'a, IDX, V> {
168+
/// Gets a reference to the value in the entry.
169+
pub fn get(&self) -> &V {
170+
self.slot.as_ref().expect("Occupied")
171+
}
172+
173+
/// Gets a mutable reference to the value in the entry.
174+
pub fn get_mut(&mut self) -> &mut V {
175+
self.slot.as_mut().expect("Occupied")
176+
}
177+
178+
/// Converts the entry into a mutable reference to its value.
179+
pub fn into_mut(self) -> &'a mut V {
180+
self.slot.as_mut().expect("Occupied")
181+
}
182+
183+
/// Sets the value of the entry with the `OccupiedEntry`’s key, and returns the entry’s old value.
184+
pub fn insert(&mut self, value: V) -> V {
185+
self.slot.replace(value).expect("Occupied")
186+
}
187+
188+
/// Takes the value of the entry out of the map, and returns it.
189+
pub fn remove(self) -> V {
190+
self.slot.take().expect("Occupied")
191+
}
192+
}

0 commit comments

Comments
 (0)