Skip to main content

orx_pinned_vec/
imp_vec.rs

1use crate::PinnedVec;
2use core::ops::{Deref, DerefMut};
3use core::{cell::UnsafeCell, marker::PhantomData};
4use orx_self_or::SoM;
5
6/// `ImpVec`, stands for immutable push vector 👿, is a data structure which allows appending elements with a shared reference.
7///
8/// Specifically, it extends vector capabilities with the following three methods:
9///
10/// * `fn imp_push(&self, value: T)`
11/// * `fn imp_extend_from_slice(&self, slice: &[T])`
12/// * `fn imp_push_get_ref(&self, value: T) -> &T`
13///
14/// Note that both of these methods can be called with `&self` rather than `&mut self`.
15/// This is safe since growth does not cause memory locations of existing elements of pinned vectors.
16///
17/// # Examples
18///
19/// A common use case is when we want to iterate over existing elements,
20/// and add new elements to the same vector.
21///
22/// The following code does not compile:
23///
24/// ```ignore
25/// fn add_doubles_of_evens(vec: &mut Vec<u32>) {
26///     for i in vec.iter().copied() {
27///         if i.is_multiple_of(2) {
28///             let doubled = 2 * i;
29///             vec.push(doubled); // cannot borrow `*vec` as mutable because it is also borrowed as immutable
30///         }
31///     }
32/// }
33///
34/// let mut vec = vec![9, 10, 11];
35///
36/// add_doubles_of_evens(&mut vec);
37///
38/// assert_eq!(&vec, &[9, 10, 11, 20]);
39/// ```
40///
41/// However, this would safely work with a pinned vector.
42/// `SplitVec` is one pinned vector implementation, see `orx-split-vec` crate for details.
43///
44/// ```ignore
45/// use orx_pinned_vec::*;
46///
47/// fn add_doubles_of_evens(vec: &mut SplitVec<u32>) {
48///     let vec = vec.as_imp_vec();
49///     for i in vec.iter().copied() {
50///         if i.is_multiple_of(2) {
51///             let doubled = 2 * i;
52///             vec.imp_push(doubled);
53///         }
54///     }
55/// }
56///
57/// let mut vec = SplitVec::new();
58/// vec.extend_from_slice(&[9, 10, 11]);
59///
60/// add_doubles_of_evens(&mut vec);
61///
62/// assert_eq!(&vec, &[9, 10, 11, 20]);
63/// ```
64pub struct ImpVec<T, P, S>
65where
66    P: PinnedVec<T>,
67    S: SoM<P>,
68{
69    pinned_vec: UnsafeCell<S>,
70    phantom: PhantomData<(T, P)>,
71}
72
73impl<T, P, S> ImpVec<T, P, S>
74where
75    P: PinnedVec<T>,
76    S: SoM<P>,
77{
78    // helper
79
80    #[allow(clippy::mut_from_ref)]
81    #[inline(always)]
82    fn pinned_mut(&self) -> &mut P {
83        // SAFETY: `ImpVec` does not implement Send or Sync.
84        // Further `imp_push` and `imp_extend_from_slice` methods are safe to call with a shared reference due to pinned vector guarantees.
85        // All other calls to this internal method require a mutable reference.
86        unsafe { &mut *self.pinned_vec.get() }.get_mut()
87    }
88
89    #[inline(always)]
90    fn pinned(&self) -> &P {
91        // SAFETY: `ImpVec` does not implement Send or Sync.
92        // Further `imp_push` and `imp_extend_from_slice` methods are safe to call with a shared reference due to pinned vector guarantees.
93        // All other calls to this internal method require a mutable reference.
94        unsafe { &*self.pinned_vec.get() }.get_ref()
95    }
96
97    // new
98
99    pub(super) fn new(pinned_vec: S) -> Self {
100        Self {
101            pinned_vec: pinned_vec.into(),
102            phantom: PhantomData,
103        }
104    }
105
106    // api
107
108    /// Returns back the inner pinned vector that this `ImpVec` is created from.
109    pub fn into_inner(self) -> S {
110        self.pinned_vec.into_inner()
111    }
112
113    /// Pushes the `value` to the vector.
114    /// This method differs from the `push` method with the required reference.
115    /// Unlike `push`, `imp_push` allows to push the element with a shared reference.
116    ///
117    /// # Example
118    ///
119    /// ```rust ignore
120    /// use pinned_vec::*;
121    ///
122    /// let mut split_vec = SplitVec::new();
123    ///
124    /// let mut vec = split_vec.as_imp_vec();
125    ///
126    /// // regular push with &mut self
127    /// vec.push(42);
128    ///
129    /// // hold on to a reference to the first element
130    /// let ref_to_first = &vec[0];
131    /// assert_eq!(ref_to_first, &42);
132    ///
133    /// // imp_push with &self
134    /// vec.imp_push(7);
135    ///
136    /// // due to `PinnedVec` guarantees, this push will never invalidate prior references
137    /// assert_eq!(ref_to_first, &42);
138    /// ```
139    #[inline(always)]
140    pub fn imp_push(&self, value: T) {
141        self.pinned_mut().push(value);
142    }
143
144    /// Pushes the `value` to the vector and returns a reference to it.
145    ///
146    /// It is the composition of [`vec.imp_push(value)`] call followed by `&vec[vec.len() - 1]`.
147    ///
148    /// [`vec.imp_push(value)`]: crate::ImpVec::imp_push
149    ///
150    /// # Examples
151    ///
152    /// This method provides a shorthand for the following common use case.
153    ///
154    /// ```rust ignore
155    /// use pinned_vec::*;
156    ///
157    /// let mut split_vec = SplitVec::new();
158    ///
159    /// let mut vec = split_vec.as_imp_vec();
160    ///
161    /// vec.imp_push('a');
162    /// let a = &vec[vec.len() - 1];
163    /// assert_eq!(a, &'a');
164    ///
165    /// // or with imp_push_get_ref
166    ///
167    /// let b = vec.imp_push_get_ref('b');
168    /// assert_eq!(b, &'b');
169    /// ```
170    #[inline(always)]
171    pub fn imp_push_get_ref(&self, value: T) -> &T {
172        let pinned = self.pinned_mut();
173        pinned.push(value);
174        &pinned[pinned.len() - 1]
175    }
176
177    /// Extends the vector with the given `slice`.
178    /// This method differs from the `extend_from_slice` method with the required reference.
179    /// Unlike `extend_from_slice`, `imp_extend_from_slice` allows to push the element with a shared reference.
180    ///
181    /// # Example
182    ///
183    /// ```rust ignore
184    /// use pinned_vec::*;
185    ///
186    /// let mut split_vec = SplitVec::new();
187    ///
188    /// // regular extend_from_slice with &mut self
189    /// vec.extend_from_slice(&[42]);
190    ///
191    /// // hold on to a reference to the first element
192    /// let ref_to_first = &vec[0];
193    /// assert_eq!(ref_to_first, &42);
194    ///
195    /// // imp_extend_from_slice with &self
196    /// vec.imp_extend_from_slice(&[0, 1, 2, 3]);
197    /// assert_eq!(vec.len(), 5);
198    ///
199    /// // due to `PinnedVec` guarantees, this extend will never invalidate prior references
200    /// assert_eq!(ref_to_first, &42);
201    /// ```
202    pub fn imp_extend_from_slice(&self, slice: &[T])
203    where
204        T: Clone,
205    {
206        self.pinned_mut().extend_from_slice(slice);
207    }
208}
209
210impl<T, P, S> Deref for ImpVec<T, P, S>
211where
212    P: PinnedVec<T>,
213    S: SoM<P>,
214{
215    type Target = P;
216    fn deref(&self) -> &Self::Target {
217        self.pinned()
218    }
219}
220
221impl<T, P, S> DerefMut for ImpVec<T, P, S>
222where
223    P: PinnedVec<T>,
224    S: SoM<P>,
225{
226    fn deref_mut(&mut self) -> &mut Self::Target {
227        self.pinned_mut()
228    }
229}