Thanks to visit codestin.com
Credit goes to docs.rs

orx_imp_vec/
imp_vec.rs

1use core::{cell::UnsafeCell, marker::PhantomData};
2use orx_pinned_vec::PinnedVec;
3use orx_split_vec::SplitVec;
4
5/// `ImpVec`, stands for immutable push vector 👿, is a data structure which allows appending elements with a shared reference.
6///
7/// Specifically, it extends vector capabilities with the following two methods:
8/// * `fn imp_push(&self, value: T)`
9/// * `fn imp_extend_from_slice(&self, slice: &[T])`
10///
11/// Note that both of these methods can be called with `&self` rather than `&mut self`.
12///
13/// # Motivation
14///
15/// Appending to a vector with a shared reference sounds unconventional, and it is.
16/// However, if we consider our vector as a bag of or a container of things rather than having a collective meaning;
17/// then, appending element or elements to the end of the vector:
18/// * does not mutate any of already added elements, and hence,
19/// * **it is not different than creating a new element in the scope**.
20///
21/// # Safety
22///
23/// It is natural to expect that appending elements to a vector does not affect already added elements.
24/// However, this is usually not the case due to underlying memory management.
25/// For instance, `std::vec::Vec` may move already added elements to different memory locations to maintain the contagious layout of the vector.
26///
27/// `PinnedVec` prevents such implicit changes in memory locations.
28/// It guarantees that push and extend methods keep memory locations of already added elements intact.
29/// Therefore, it is perfectly safe to hold on to references of the vector while appending elements.
30///
31/// Consider the classical example that does not compile, which is often presented to highlight the safety guarantees of rust:
32///
33/// ```rust
34/// let mut vec = vec![0, 1, 2, 3];
35///
36/// let ref_to_first = &vec[0];
37/// assert_eq!(ref_to_first, &0);
38///
39/// vec.push(4);
40///
41/// // does not compile due to the following reason:  cannot borrow `vec` as mutable because it is also borrowed as immutable
42/// // assert_eq!(ref_to_first, &0);
43/// ```
44///
45/// This wonderful feature of the borrow checker of rust is not required and used for `imp_push` and `imp_extend_from_slice` methods of `ImpVec`
46/// since these methods do not require a `&mut self` reference.
47/// Therefore, the following code compiles and runs perfectly safely.
48///
49/// ```rust
50/// use orx_imp_vec::*;
51///
52/// let mut vec = ImpVec::new();
53/// vec.extend_from_slice(&[0, 1, 2, 3]);
54///
55/// let ref_to_first = &vec[0];
56/// assert_eq!(ref_to_first, &0);
57///
58/// vec.imp_push(4);
59/// assert_eq!(vec.len(), 5);
60///
61/// vec.imp_extend_from_slice(&[6, 7]);
62/// assert_eq!(vec.len(), 7);
63///
64/// assert_eq!(ref_to_first, &0);
65/// ```
66pub struct ImpVec<T, P = SplitVec<T>>
67where
68    P: PinnedVec<T>,
69{
70    pub(crate) pinned_vec: UnsafeCell<P>,
71    pub(crate) phantom: PhantomData<T>,
72}
73
74impl<T, P: PinnedVec<T>> ImpVec<T, P> {
75    /// Consumes the imp-vec into the wrapped inner pinned vector.
76    ///
77    /// # Example
78    ///
79    /// ```rust
80    /// use orx_split_vec::SplitVec;
81    /// use orx_imp_vec::ImpVec;
82    ///
83    /// let pinned_vec = SplitVec::new();
84    ///
85    /// let imp_vec = ImpVec::from(pinned_vec);
86    /// imp_vec.imp_push(42);
87    ///
88    /// let pinned_vec = imp_vec.into_inner();
89    /// assert_eq!(&pinned_vec, &[42]);
90    /// ```
91    pub fn into_inner(self) -> P {
92        self.pinned_vec.into_inner()
93    }
94
95    /// Pushes the `value` to the vector.
96    /// This method differs from the `push` method with the required reference.
97    /// Unlike `push`, `imp_push` allows to push the element with a shared reference.
98    ///
99    /// # Example
100    ///
101    /// ```rust
102    /// use orx_imp_vec::*;
103    ///
104    /// let mut vec = ImpVec::new();
105    ///
106    /// // regular push with &mut self
107    /// vec.push(42);
108    ///
109    /// // hold on to a reference to the first element
110    /// let ref_to_first = &vec[0];
111    /// assert_eq!(ref_to_first, &42);
112    ///
113    /// // imp_push with &self
114    /// vec.imp_push(7);
115    ///
116    /// // due to `PinnedVec` guarantees, this push will never invalidate prior references
117    /// assert_eq!(ref_to_first, &42);
118    /// ```
119    ///
120    /// # Safety
121    ///
122    /// Wrapping a `PinnedVec` with an `ImpVec` provides with two additional methods: `imp_push` and `imp_extend_from_slice`.
123    /// Note that these push and extend methods grow the vector by appending elements to the end.
124    ///
125    /// It is natural to expect that these operations do not change the memory locations of already added elements.
126    /// However, this is usually not the case due to underlying allocations.
127    /// For instance, `std::vec::Vec` may move already added elements in memory to maintain the contagious layout of the vector.
128    ///
129    /// `PinnedVec` prevents such implicit changes in memory locations.
130    /// It guarantees that push and extend methods keep memory locations of already added elements intact.
131    /// Therefore, it is perfectly safe to hold on to references of the vector while appending elements.
132    ///
133    /// Consider the classical example that does not compile, which is often presented to highlight the safety guarantees of rust:
134    ///
135    /// ```rust
136    /// let mut vec = vec![0, 1, 2, 3];
137    ///
138    /// let ref_to_first = &vec[0];
139    /// assert_eq!(ref_to_first, &0);
140    ///
141    /// vec.push(4);
142    ///
143    /// // does not compile due to the following reason:  cannot borrow `vec` as mutable because it is also borrowed as immutable
144    /// // assert_eq!(ref_to_first, &0);
145    /// ```
146    ///
147    /// This wonderful feature of the borrow checker of rust is not required and used for `imp_push` and `imp_extend_from_slice` methods of `ImpVec`
148    /// since these methods do not require a `&mut self` reference.
149    /// Therefore, the following code compiles and runs perfectly safely.
150    ///
151    /// ```rust
152    /// use orx_imp_vec::*;
153    ///
154    /// let mut vec = ImpVec::new();
155    /// vec.extend_from_slice(&[0, 1, 2, 3]);
156    ///
157    /// let ref_to_first = &vec[0];
158    /// assert_eq!(ref_to_first, &0);
159    ///
160    /// vec.imp_push(4);
161    /// assert_eq!(vec.len(), 5);
162    ///
163    /// assert_eq!(ref_to_first, &0);
164    /// ```
165    ///
166    /// Although unconventional, this makes sense when we consider the `ImpVec` as a bag or container of things, rather than having a collective meaning.
167    /// In other words, when we do not rely on reduction methods, such as `count` or `sum`, appending element or elements to the end of the vector:
168    /// * does not mutate any of already added elements, and hence,
169    /// * **it is not different than creating a new element in the scope**.
170    pub fn imp_push(&self, value: T) {
171        self.pinned_mut().push(value);
172    }
173
174    /// Pushes the `value` to the vector and returns a reference to it.
175    ///
176    /// It is the composition of [`vec.imp_push(value)`] call followed by `&vec[vec.len() - 1]`.
177    ///
178    /// [`vec.imp_push(value)`]: crate::ImpVec::imp_push
179    ///
180    /// # Examples
181    ///
182    /// This method provides a shorthand for the following common use case.
183    ///
184    /// ```
185    /// use orx_imp_vec::*;
186    ///
187    /// let vec = ImpVec::new();
188    ///
189    /// vec.imp_push('a');
190    /// let a = &vec[vec.len() - 1];
191    /// assert_eq!(a, &'a');
192    ///
193    /// // or with imp_push_get_ref
194    ///
195    /// let b = vec.imp_push_get_ref('b');
196    /// assert_eq!(b, &'b');
197    /// ```
198    pub fn imp_push_get_ref(&self, value: T) -> &T {
199        let pinned = self.pinned_mut();
200        pinned.push(value);
201        &pinned[pinned.len() - 1]
202    }
203
204    /// Extends the vector with the given `slice`.
205    /// This method differs from the `extend_from_slice` method with the required reference.
206    /// Unlike `extend_from_slice`, `imp_extend_from_slice` allows to push the element with a shared reference.
207    ///
208    /// # Example
209    ///
210    /// ```rust
211    /// use orx_imp_vec::*;
212    ///
213    /// let mut vec = ImpVec::new();
214    ///
215    /// // regular extend_from_slice with &mut self
216    /// vec.extend_from_slice(&[42]);
217    ///
218    /// // hold on to a reference to the first element
219    /// let ref_to_first = &vec[0];
220    /// assert_eq!(ref_to_first, &42);
221    ///
222    /// // imp_extend_from_slice with &self
223    /// vec.imp_extend_from_slice(&[0, 1, 2, 3]);
224    /// assert_eq!(vec.len(), 5);
225    ///
226    /// // due to `PinnedVec` guarantees, this extend will never invalidate prior references
227    /// assert_eq!(ref_to_first, &42);
228    /// ```
229    ///
230    /// # Safety
231    ///
232    /// Wrapping a `PinnedVec` with an `ImpVec` provides with two additional methods: `imp_push` and `imp_extend_from_slice`.
233    /// Note that these push and extend methods grow the vector by appending elements to the end.
234    ///
235    /// It is natural to expect that these operations do not change the memory locations of already added elements.
236    /// However, this is usually not the case due to underlying allocations.
237    /// For instance, `std::vec::Vec` may move already added elements in memory to maintain the contagious layout of the vector.
238    ///
239    /// `PinnedVec` prevents such implicit changes in memory locations.
240    /// It guarantees that push and extend methods keep memory locations of already added elements intact.
241    /// Therefore, it is perfectly safe to hold on to references of the vector while appending elements.
242    ///
243    /// Consider the classical example that does not compile, which is often presented to highlight the safety guarantees of rust:
244    ///
245    /// ```rust
246    /// let mut vec = vec![0];
247    ///
248    /// let ref_to_first = &vec[0];
249    /// assert_eq!(ref_to_first, &0);
250    ///
251    /// vec.extend_from_slice(&[1, 2, 3, 4]);
252    ///
253    /// // does not compile due to the following reason:  cannot borrow `vec` as mutable because it is also borrowed as immutable
254    /// // assert_eq!(ref_to_first, &0);
255    /// ```
256    ///
257    /// This wonderful feature of the borrow checker of rust is not required and used for `imp_push` and `imp_extend_from_slice` methods of `ImpVec`
258    /// since these methods do not require a `&mut self` reference.
259    /// Therefore, the following code compiles and runs perfectly safely.
260    ///
261    /// ```rust
262    /// use orx_imp_vec::*;
263    ///
264    /// let mut vec = ImpVec::new();
265    /// vec.push(0);
266    ///
267    /// let ref_to_first = &vec[0];
268    /// assert_eq!(ref_to_first, &0);
269    ///
270    /// vec.imp_extend_from_slice(&[1, 2, 3, 4]);
271    ///
272    /// assert_eq!(ref_to_first, &0);
273    /// ```
274    ///
275    /// Although unconventional, this makes sense when we consider the `ImpVec` as a bag or container of things, rather than having a collective meaning.
276    /// In other words, when we do not rely on reduction methods, such as `count` or `sum`, appending element or elements to the end of the vector:
277    /// * does not mutate any of already added elements, and hence,
278    /// * **it is not different than creating a new element in the scope**.
279    pub fn imp_extend_from_slice(&self, slice: &[T])
280    where
281        T: Clone,
282    {
283        self.pinned_mut().extend_from_slice(slice);
284    }
285
286    // helper
287    #[allow(clippy::mut_from_ref)]
288    pub(crate) fn pinned_mut(&self) -> &mut P {
289        // SAFETY: `ImpVec` does not implement Send or Sync.
290        // Further `imp_push` and `imp_extend_from_slice` methods are safe to call with a shared reference due to pinned vector guarantees.
291        // All other calls to this internal method require a mutable reference.
292        unsafe { &mut *self.pinned_vec.get() }
293    }
294
295    pub(crate) fn pinned(&self) -> &P {
296        // SAFETY: `ImpVec` does not implement Send or Sync.
297        // Further `imp_push` and `imp_extend_from_slice` methods are safe to call with a shared reference due to pinned vector guarantees.
298        // All other calls to this internal method require a mutable reference.
299        unsafe { &*self.pinned_vec.get() }
300    }
301}