Skip to main content

tagged_vec/
lib.rs

1//! An alternative to the standard libraries' [`Vec`] which is indexed with a custom type instead of [`usize`].
2//!
3//! This is useful to catch errors like using the wrong variable to index the vector.
4
5#![warn(missing_docs)]
6
7use std::{
8    marker::PhantomData,
9    ops::{Bound, RangeBounds},
10};
11
12use mapped_range_bounds::MappedRangeBounds;
13
14#[cfg(feature = "binary-io")]
15mod binary_io;
16mod mapped_range_bounds;
17#[cfg(test)]
18mod tests;
19mod trait_impls;
20
21/// A [`Vec`] wrapper that allows indexing only via the given `Index` type.
22///
23/// For actual operation, `Index` must implement [`From<usize>`] and [`Into<usize>`].
24pub struct TaggedVec<Index, Value> {
25    index_type: PhantomData<Index>,
26    vec: Vec<Value>,
27}
28
29impl<Index, Value> TaggedVec<Index, Value> {
30    /// Creates a new empty `TaggedVec`.
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Creates a new empty `TaggedVec` with at least the specified capacity.
36    pub fn with_capacity(capacity: usize) -> Self {
37        Self {
38            index_type: PhantomData,
39            vec: Vec::with_capacity(capacity),
40        }
41    }
42
43    /// Returns the number of elements in the `TaggedVec`.
44    pub fn len(&self) -> usize {
45        self.vec.len()
46    }
47
48    /// Returns `true` if the `TaggedVec` contains no elements.
49    pub fn is_empty(&self) -> bool {
50        self.vec.is_empty()
51    }
52
53    /// Returns the total number of elements the `TaggedVec` can hold without reallocating.
54    pub fn capacity(&self) -> usize {
55        self.vec.capacity()
56    }
57
58    /// Returns the untagged slice of the `Vec` underlying this `TaggedVec`.
59    pub fn as_untagged_slice(&self) -> &[Value] {
60        &self.vec
61    }
62
63    /// Returns the untagged mutable slice of the `Vec` underlying this `TaggedVec`.
64    pub fn as_untagged_mut_slice(&mut self) -> &mut [Value] {
65        &mut self.vec
66    }
67
68    /// Inserts the given value at the back of the `TaggedVec`, returning its index.
69    pub fn push(&mut self, value: Value) -> Index
70    where
71        Index: From<usize>,
72    {
73        let index = self.vec.len().into();
74        self.vec.push(value);
75        index
76    }
77
78    /// Insert a single value into the `TaggedVec` by constructing it in place.
79    ///
80    /// This method allows to create the value while already knowing its index.
81    /// Returns the index.
82    pub fn push_in_place(&mut self, value: impl FnOnce(Index) -> Value) -> Index
83    where
84        Index: From<usize>,
85    {
86        let index = self.vec.len();
87        self.vec.push(value(index.into()));
88        index.into()
89    }
90
91    /// Removes the value at the back of the `TaggedVec` and returns it with its index.
92    pub fn pop(&mut self) -> Option<(Index, Value)>
93    where
94        Index: From<usize>,
95    {
96        if let Some(value) = self.vec.pop() {
97            Some((self.vec.len().into(), value))
98        } else {
99            None
100        }
101    }
102
103    /// Inserts the given `value` at position `index`, shifting all existing values in range `index..` one position to the right.
104    pub fn insert(&mut self, index: Index, value: Value)
105    where
106        Index: Into<usize>,
107    {
108        self.vec.insert(index.into(), value);
109    }
110
111    /// See [`Vec::splice`].
112    pub fn splice<I: IntoIterator<Item = Value>>(
113        &mut self,
114        range: impl RangeBounds<Index>,
115        replace_with: I,
116    ) -> std::vec::Splice<'_, I::IntoIter>
117    where
118        usize: From<Index>,
119        Index: Copy,
120    {
121        self.vec.splice(MappedRangeBounds::new(range), replace_with)
122    }
123
124    /// Retains only the values specified by the predicate.
125    ///
126    /// In other words, remove all values `v` for which `f(&v)` returns `false`.
127    /// This method operates in place, visiting each value exactly once in the original order, and preserves the order of the retained values.
128    pub fn retain(&mut self, f: impl FnMut(&Value) -> bool) {
129        self.vec.retain(f);
130    }
131
132    /// Removes the elements at the specified indices, shifting other elements to the left to fill gaps as required.
133    ///
134    /// The provided indices must be sorted.
135    pub fn remove_multi(&mut self, indices: impl IntoIterator<Item = Index>)
136    where
137        Index: Into<usize> + Clone,
138    {
139        let mut indices = indices.into_iter().peekable();
140        let mut current_index = 0;
141        self.vec.retain(|_| {
142            if let Some(next_delete_index) = indices.peek() {
143                let next_delete_index = next_delete_index.clone().into();
144                let result = if next_delete_index == current_index {
145                    indices.next();
146
147                    if let Some(next_next_delete_index) = indices.peek() {
148                        let next_next_delete_index: usize = next_next_delete_index.clone().into();
149                        assert!(next_next_delete_index > next_delete_index);
150                    }
151
152                    false
153                } else {
154                    true
155                };
156                current_index += 1;
157                result
158            } else {
159                true
160            }
161        });
162
163        assert!(indices.next().is_none());
164    }
165
166    /// Returns a reference to the value at the given index, or `None` if the index is out of bounds.
167    pub fn get(&self, index: Index) -> Option<&Value>
168    where
169        Index: Into<usize>,
170    {
171        self.vec.get(index.into())
172    }
173
174    /// Returns an iterator over references to the entries of the `TaggedVec`.
175    ///
176    /// The `range` specifies which subset of the entries to iterate over.
177    /// Iterating over a partial range with this argument is more efficient than iterating over the whole vector and skipping the unwanted entries, because the returned iterator does not support the skip optimisation.
178    pub fn iter(
179        &self,
180        range: impl RangeBounds<Index>,
181    ) -> impl DoubleEndedIterator<Item = (Index, &Value)> + ExactSizeIterator
182    where
183        Index: From<usize> + Copy,
184        usize: From<Index>,
185    {
186        let range = MappedRangeBounds::new(range);
187        let start_index_inclusive = match range.start_bound() {
188            Bound::Included(index) => *index,
189            Bound::Excluded(index) => (*index).checked_add(1).expect("start index overflow"),
190            Bound::Unbounded => 0,
191        };
192        let end_index_exclusive = match range.end_bound() {
193            Bound::Included(index) => (*index).checked_add(1).expect("end index overflow"),
194            Bound::Excluded(index) => *index,
195            Bound::Unbounded => self.vec.len(),
196        };
197
198        self.vec
199            .iter()
200            .enumerate()
201            .skip(start_index_inclusive)
202            .take(end_index_exclusive - start_index_inclusive)
203            .map(|(index, value)| (index.into(), value))
204    }
205
206    /// Returns an iterator over mutable references to the entries of the `TaggedVec`.
207    ///
208    /// The `range` specifies which subset of the entries to iterate over.
209    /// Iterating over a partial range with this argument is more efficient than iterating over the whole vector and skipping the unwanted entries, because the returned iterator does not support the skip optimisation.
210    pub fn iter_mut(
211        &mut self,
212        range: impl RangeBounds<Index>,
213    ) -> impl DoubleEndedIterator<Item = (Index, &mut Value)> + ExactSizeIterator
214    where
215        Index: From<usize> + Copy,
216        usize: From<Index>,
217    {
218        let range = MappedRangeBounds::new(range);
219        let start_index_inclusive = match range.start_bound() {
220            Bound::Included(index) => *index,
221            Bound::Excluded(index) => (*index).checked_add(1).expect("start index overflow"),
222            Bound::Unbounded => 0,
223        };
224        let end_index_exclusive = match range.end_bound() {
225            Bound::Included(index) => (*index).checked_add(1).expect("end index overflow"),
226            Bound::Excluded(index) => *index,
227            Bound::Unbounded => self.vec.len(),
228        };
229
230        self.vec
231            .iter_mut()
232            .enumerate()
233            .skip(start_index_inclusive)
234            .take(end_index_exclusive - start_index_inclusive)
235            .map(|(index, value)| (index.into(), value))
236    }
237
238    /// Returns an iterator over references to the values of the `TaggedVec`.
239    pub fn iter_values(&self) -> std::slice::Iter<'_, Value> {
240        self.vec.iter()
241    }
242
243    /// Returns an iterator over mutable references to the values of the `TaggedVec`.
244    pub fn iter_values_mut(&mut self) -> std::slice::IterMut<'_, Value> {
245        self.vec.iter_mut()
246    }
247
248    /// Returns an iterator over the indices of the `TaggedVec`.
249    ///
250    /// The `range` specifies which subset of the entries to iterate over.
251    /// Iterating over a partial range with this argument is more efficient than iterating over the whole vector and skipping the unwanted entries, because the returned iterator does not support the skip optimisation.
252    pub fn iter_indices(
253        &self,
254        range: impl RangeBounds<Index>,
255    ) -> impl DoubleEndedIterator<Item = Index> + ExactSizeIterator
256    where
257        Index: From<usize> + Copy,
258        usize: From<Index>,
259    {
260        let range = MappedRangeBounds::new(range);
261        let start_index_inclusive = match range.start_bound() {
262            Bound::Included(index) => *index,
263            Bound::Excluded(index) => (*index).checked_add(1).expect("start index overflow"),
264            Bound::Unbounded => 0,
265        };
266        let end_index_exclusive = match range.end_bound() {
267            Bound::Included(index) => (*index).checked_add(1).expect("end index overflow"),
268            Bound::Excluded(index) => *index,
269            Bound::Unbounded => self.vec.len(),
270        };
271
272        (start_index_inclusive..end_index_exclusive).map(Into::into)
273    }
274
275    /// Consumes the `TaggedVec`, returning an iterator over the entries.
276    ///
277    /// The `range` specifies which subset of the entries to iterate over.
278    /// Iterating over a partial range with this argument is more efficient than iterating over the whole vector and skipping the unwanted entries, because the returned iterator does not support the skip optimisation.
279    pub fn into_iter(
280        self,
281        range: impl RangeBounds<Index>,
282    ) -> impl DoubleEndedIterator<Item = (Index, Value)> + ExactSizeIterator
283    where
284        Index: From<usize> + Copy,
285        usize: From<Index>,
286    {
287        let range = MappedRangeBounds::new(range);
288        let start_index_inclusive = match range.start_bound() {
289            Bound::Included(index) => *index,
290            Bound::Excluded(index) => (*index).checked_add(1).expect("start index overflow"),
291            Bound::Unbounded => 0,
292        };
293        let end_index_exclusive = match range.end_bound() {
294            Bound::Included(index) => (*index).checked_add(1).expect("end index overflow"),
295            Bound::Excluded(index) => *index,
296            Bound::Unbounded => self.vec.len(),
297        };
298
299        self.vec
300            .into_iter()
301            .enumerate()
302            .skip(start_index_inclusive)
303            .take(end_index_exclusive - start_index_inclusive)
304            .map(|(index, value)| (index.into(), value))
305    }
306
307    /// Consumes the `TaggedVec`, returning an iterator over the values.
308    pub fn into_values_iter(self) -> std::vec::IntoIter<Value> {
309        self.vec.into_iter()
310    }
311}