dyn_slice/
dyn_slice.rs

1use core::{
2    marker::PhantomData,
3    mem::transmute,
4    ops::{Bound, Index, RangeBounds},
5    ptr,
6    ptr::{DynMetadata, Pointee},
7    slice,
8};
9
10use crate::Iter;
11
12/// `&dyn [Trait]`
13///
14/// A type erased slice of elements that implement a trait.
15///
16/// # Example
17/// ```
18/// use dyn_slice::standard::debug;
19///
20/// let slice = debug::new(&[1, 2, 3, 4, 5]);
21/// # assert_eq!(&format!("{slice:?}"), "[1, 2, 3, 4, 5]");
22/// println!("{slice:?}"); // [1, 2, 3, 4, 5]
23/// ```
24pub struct DynSlice<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> {
25    pub(crate) vtable_ptr: *const (),
26    pub(crate) len: usize,
27    pub(crate) data: *const (),
28    phantom: PhantomData<&'a Dyn>,
29}
30
31impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> Clone for DynSlice<'a, Dyn> {
32    fn clone(&self) -> Self {
33        *self
34    }
35}
36impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> Copy for DynSlice<'a, Dyn> {}
37
38impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> DynSlice<'a, Dyn> {
39    #[inline]
40    #[must_use]
41    /// Construct a dyn slice given a slice and a vtable pointer.
42    ///
43    /// # Safety
44    /// Caller must ensure that `vtable_ptr` is a valid instance of `DynMetadata` for `DynSliceFromType` and `Dyn` transmuted, or optionally, a null pointer if `value.len() == 0`.
45    pub const unsafe fn with_vtable_ptr<DynSliceFromType>(
46        value: &'a [DynSliceFromType],
47        vtable_ptr: *const (),
48    ) -> Self {
49        Self {
50            vtable_ptr,
51            len: value.len(),
52            data: value.as_ptr().cast(),
53            phantom: PhantomData,
54        }
55    }
56
57    #[inline]
58    #[must_use]
59    /// Construct a dyn slice given a slice and a `DynMetadata` instance.
60    ///
61    /// # Safety
62    /// Caller must ensure that `metadata` is a valid instance of `DynMetadata` for `DynSliceFromType` and `Dyn`.
63    pub const unsafe fn with_metadata<DynSliceFromType>(
64        value: &'a [DynSliceFromType],
65        metadata: DynMetadata<Dyn>,
66    ) -> Self {
67        Self::with_vtable_ptr(value, transmute(metadata))
68    }
69
70    #[inline]
71    #[must_use]
72    /// Construct a dyn slice from raw parts.
73    ///
74    /// # Safety
75    /// Caller must ensure that:
76    /// - `vtable_ptr` is a valid instance of `DynMetadata` transmuted, or optionally, a null pointer if `len == 0`,
77    /// - `len` <= the length of the slice in memory from the `data` pointer,
78    /// - `data` is a valid pointer to the slice,
79    /// - the underlying slice is the same layout as [`[T]`](https://doc.rust-lang.org/reference/type-layout.html#slice-layout)
80    pub const unsafe fn from_parts(vtable_ptr: *const (), len: usize, data: *const ()) -> Self {
81        Self {
82            vtable_ptr,
83            len,
84            data,
85            phantom: PhantomData,
86        }
87    }
88
89    #[inline]
90    #[must_use]
91    /// Construct a dyn slice from raw parts with a `DynMetadata` instance rather than a vtable pointer.
92    ///
93    /// # Safety
94    /// Caller must ensure that:
95    /// - `metadata` is a valid instance of `DynMetadata`,
96    /// - `len` <= the length of the slice in memory from the `data` pointer,
97    /// - `data` is a valid pointer to the slice,
98    /// - the underlying slice is the same layout as [`[T]`](https://doc.rust-lang.org/reference/type-layout.html#slice-layout)
99    pub unsafe fn from_parts_with_metadata(
100        metadata: DynMetadata<Dyn>,
101        len: usize,
102        data: *const (),
103    ) -> Self {
104        Self::from_parts(transmute(metadata), len, data)
105    }
106
107    #[inline]
108    #[must_use]
109    /// Get the vtable pointer, which may be null if the slice is empty.
110    pub const fn vtable_ptr(&self) -> *const () {
111        self.vtable_ptr
112    }
113
114    #[inline]
115    #[must_use]
116    /// Get the metadata component of the element's pointers, or possibly `None` if the slice is empty.
117    pub fn metadata(&self) -> Option<DynMetadata<Dyn>> {
118        let vtable_ptr = self.vtable_ptr();
119        (!vtable_ptr.is_null()).then(|| {
120            // SAFETY:
121            // DynMetadata only contains a single pointer, and has the same layout as *const ().
122            // The statement above guarantees that the pointer is not null and so, the pointer is
123            // guaranteed to point to a vtable by the safe methods that create the slice.
124            unsafe { transmute(vtable_ptr) }
125        })
126    }
127
128    #[inline]
129    #[must_use]
130    /// Returns the number of elements in the slice.
131    ///
132    /// # Example
133    /// ```
134    /// use dyn_slice::standard::debug;
135    ///
136    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
137    /// assert_eq!(slice.len(), 5);
138    /// ```
139    pub const fn len(&self) -> usize {
140        self.len
141    }
142
143    #[inline]
144    #[must_use]
145    /// Returns a pointer to the underlying slice, which may be null if the slice is empty.
146    pub const fn as_ptr(&self) -> *const () {
147        self.data
148    }
149
150    #[inline]
151    #[must_use]
152    /// Returns `true` if the slice has a length of 0.
153    ///
154    /// # Example
155    /// ```
156    /// use dyn_slice::standard::debug;
157    ///
158    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
159    /// assert!(!slice.is_empty());
160    ///
161    /// let empty_slice = debug::new::<u8>(&[]);
162    /// assert!(empty_slice.is_empty());
163    /// ```
164    pub const fn is_empty(&self) -> bool {
165        self.len == 0
166    }
167
168    #[inline]
169    #[must_use]
170    /// Returns a reference to the first element, without doing bounds checking.
171    ///
172    /// # Safety
173    /// The caller must ensure that `!self.is_empty()`
174    /// Calling this on an empty `DynSlice` will result in a segfault!
175    pub unsafe fn first_unchecked(&self) -> &Dyn {
176        debug_assert!(!self.is_empty(), "[dyn-slice] slice is empty!");
177        debug_assert!(
178            !self.vtable_ptr.is_null(),
179            "[dyn-slice] vtable pointer is null on access!"
180        );
181
182        &*ptr::from_raw_parts::<Dyn>(self.as_ptr(), transmute(self.vtable_ptr()))
183    }
184
185    #[must_use]
186    /// Returns a reference to the first element of the slice, or `None` if it is empty.
187    ///
188    /// # Example
189    /// ```
190    /// use dyn_slice::standard::debug;
191    ///
192    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
193    /// # assert_eq!(format!("{:?}", slice.first().unwrap()), "1");
194    /// println!("{:?}", slice.first()); // Some(1)
195    ///
196    /// let empty_slice = debug::new::<u8>(&[]);
197    /// # assert!(empty_slice.first().is_none());
198    /// println!("{:?}", empty_slice.first()); // None
199    /// ```
200    pub fn first(&self) -> Option<&Dyn> {
201        (!self.is_empty()).then(|| {
202            // SAFETY:
203            // The above statement ensures that slice is not empty, and
204            // therefore has a first (index 0) element and a valid vtable pointer.
205            unsafe { self.first_unchecked() }
206        })
207    }
208
209    #[must_use]
210    /// Returns a reference to the last element of the slice, or `None` if it is empty.
211    ///
212    /// # Example
213    /// ```
214    /// use dyn_slice::standard::debug;
215    ///
216    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
217    /// # assert_eq!(format!("{:?}", slice.last().unwrap()), "5");
218    /// println!("{:?}", slice.last()); // Some(5)
219    ///
220    /// let empty_slice = debug::new::<u8>(&[]);
221    /// # assert!(empty_slice.last().is_none());
222    /// println!("{:?}", empty_slice.last()); // None
223    /// ```
224    pub fn last(&self) -> Option<&Dyn> {
225        (!self.is_empty()).then(|| {
226            // SAFETY:
227            // The above statement ensures that slice is not empty, and
228            // therefore has a last (index len - 1) element and a valid vtable pointer.
229            unsafe { self.get_unchecked(self.len - 1) }
230        })
231    }
232
233    #[must_use]
234    /// Returns a reference to the element at the given `index` or `None` if the `index` is out of bounds.
235    ///
236    /// # Example
237    /// ```
238    /// use dyn_slice::standard::debug;
239    ///
240    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
241    /// # assert_eq!(format!("{:?}", slice.get(2).unwrap()), "3");
242    /// println!("{:?}", slice.get(2)); // Some(3)
243    /// # assert!(slice.get(5).is_none());
244    /// println!("{:?}", slice.get(5)); // None
245    /// ```
246    pub fn get(&self, index: usize) -> Option<&Dyn> {
247        (index < self.len).then(|| {
248            // SAFETY:
249            // The above inequality ensures that the index is less than the
250            // length, and is therefore valid. This also ensures that the slice
251            // has a valid vtable pointer because the slice guaranteed to not be empty.
252            unsafe { self.get_unchecked(index) }
253        })
254    }
255
256    #[inline]
257    #[must_use]
258    /// Returns a reference to the element at the given `index`, without doing bounds checking.
259    ///
260    /// # Safety
261    /// The caller must ensure that `index < self.len()`
262    /// Calling this on an empty dyn Slice will result in a segfault!
263    pub unsafe fn get_unchecked(&self, index: usize) -> &Dyn {
264        debug_assert!(
265            index < self.len,
266            "[dyn-slice] index is greater than length!"
267        );
268        debug_assert!(
269            !self.vtable_ptr.is_null(),
270            "[dyn-slice] vtable pointer is null on access!"
271        );
272
273        let metadata = transmute::<_, DynMetadata<Dyn>>(self.vtable_ptr());
274        &*ptr::from_raw_parts::<Dyn>(self.as_ptr().byte_add(metadata.size_of() * index), metadata)
275    }
276
277    #[inline]
278    #[must_use]
279    /// Get a sub-slice from the `start` index with the `len`, without doing bounds checking.
280    ///
281    /// # Safety
282    /// Caller must ensure that:
283    /// - `start < self.len()`
284    /// - `len <= self.len() - start`
285    pub unsafe fn slice_unchecked(&self, start: usize, len: usize) -> DynSlice<Dyn> {
286        // NOTE: DO NOT MAKE THIS FUNCTION RETURN `Self` as `Self` comes with an incorrect lifetime
287        debug_assert!(
288            start + len <= self.len,
289            "[dyn-slice] sub-slice is out of bounds!"
290        );
291
292        let metadata = transmute::<_, DynMetadata<Dyn>>(self.vtable_ptr());
293        Self::from_parts_with_metadata(
294            metadata,
295            len,
296            self.as_ptr().byte_add(metadata.size_of() * start),
297        )
298    }
299
300    #[must_use]
301    /// Returns a sub-slice from the `start` index with the `len` or `None` if the slice is out of bounds.
302    ///
303    /// # Example
304    /// ```
305    /// use dyn_slice::standard::debug;
306    ///
307    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
308    /// println!("{slice:?}"); // [1, 2, 3, 4, 5]
309    /// # assert_eq!(format!("{:?}", slice.slice(1..4).unwrap()), "[2, 3, 4]");
310    /// println!("{:?}", slice.slice(1..4)); // Some([2, 3, 4])
311    /// # assert_eq!(format!("{:?}", slice.slice(2..).unwrap()), "[3, 4, 5]");
312    /// println!("{:?}", slice.slice(2..)); // Some([3, 4, 5])
313    /// # assert_eq!(format!("{:?}", slice.slice(5..).unwrap()), "[]");
314    /// println!("{:?}", slice.slice(5..)); // Some([])
315    /// # assert!(slice.slice(6..).is_none());
316    /// println!("{:?}", slice.slice(6..)); // None
317    /// ```
318    pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> Option<DynSlice<Dyn>> {
319        // NOTE: DO NOT MAKE THIS FUNCTION RETURN `Self` as `Self` comes with an incorrect lifetime
320
321        let start_inclusive = match range.start_bound() {
322            Bound::Included(i) => *i,
323            Bound::Excluded(i) => i.checked_add(1)?,
324            Bound::Unbounded => 0,
325        };
326
327        let end_exclusive = match range.end_bound() {
328            Bound::Included(i) => i.checked_add(1)?,
329            Bound::Excluded(i) => *i,
330            Bound::Unbounded => self.len,
331        };
332
333        if end_exclusive > self.len {
334            return None;
335        }
336
337        let len = end_exclusive.checked_sub(start_inclusive)?;
338
339        // SAFETY:
340        // The above `if` statement ensures that the the end of the new slice
341        // does not exceed that of the original slice, therefore, the new
342        // slice is valid.
343        Some(unsafe { self.slice_unchecked(start_inclusive, len) })
344    }
345
346    #[inline]
347    #[must_use]
348    /// Returns the underlying slice as `&[T]`.
349    ///
350    /// # Safety
351    /// The caller must ensure that the underlying slice is of type `[T]`.
352    pub const unsafe fn downcast_unchecked<T>(&self) -> &[T] {
353        slice::from_raw_parts(self.as_ptr().cast(), self.len)
354    }
355
356    #[inline]
357    #[must_use]
358    /// Returns an iterator over the slice.
359    ///
360    /// # Example
361    /// ```
362    /// use dyn_slice::standard::debug;
363    ///
364    /// let slice = debug::new(&[1, 2, 3, 4, 5]);
365    /// let iter = slice.iter().map(|x| format!("{:?}!", x));
366    /// # assert_eq!(
367    /// #     format!("{:?}", iter.clone().collect::<Vec<String>>()),
368    /// #     r#"["1!", "2!", "3!", "4!", "5!"]"#
369    /// # );
370    /// println!("{:?}", iter.collect::<Vec<String>>()); // ["1!", "2!", "3!", "4!", "5!"]
371    /// ```
372    pub const fn iter(&self) -> Iter<'_, Dyn> {
373        Iter { slice: *self }
374    }
375}
376
377impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> Index<usize> for DynSlice<'a, Dyn> {
378    type Output = Dyn;
379
380    fn index(&self, index: usize) -> &Self::Output {
381        assert!(index < self.len, "index out of bounds");
382        debug_assert!(
383            !self.vtable_ptr.is_null(),
384            "[dyn-slice] vtable pointer is null on access!"
385        );
386
387        // SAFETY:
388        // The above assertion ensures that the index is less than the
389        // length, and is therefore valid. This also ensures that the slice
390        // has a valid vtable pointer because the slice guaranteed to not be empty.
391        unsafe { self.get_unchecked(index) }
392    }
393}
394
395impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> IntoIterator for DynSlice<'a, Dyn> {
396    type IntoIter = Iter<'a, Dyn>;
397    type Item = &'a Dyn;
398
399    #[inline]
400    fn into_iter(self) -> Self::IntoIter {
401        Iter { slice: self }
402    }
403}
404
405impl<'a, 'b, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> IntoIterator
406    for &'b DynSlice<'a, Dyn>
407{
408    type IntoIter = Iter<'b, Dyn>;
409    type Item = &'b Dyn;
410
411    #[inline]
412    fn into_iter(self) -> Self::IntoIter {
413        self.iter()
414    }
415}
416
417#[cfg(test)]
418mod test {
419    use core::{fmt::Display, ptr::addr_of};
420
421    use crate::{declare_new_fns, standard::partial_eq, DynSlice};
422
423    declare_new_fns!(
424        #[crate = crate]
425        display_dyn_slice Display
426    );
427    pub use display_dyn_slice::new as new_display_dyn_slice;
428
429    #[test]
430    fn create_dyn_slice() {
431        let array: [u8; 5] = [1, 2, 3, 4, 5];
432
433        let dyn_slice = new_display_dyn_slice(&array);
434
435        assert_eq!(dyn_slice.len(), array.len());
436        assert!(!dyn_slice.is_empty());
437
438        for (i, x) in array.iter().enumerate() {
439            assert_eq!(
440                format!(
441                    "{}",
442                    dyn_slice
443                        .get(i)
444                        .expect("failed to get an element of dyn_slice")
445                ),
446                format!("{x}"),
447            );
448        }
449    }
450
451    #[test]
452    fn empty() {
453        let array: [u8; 0] = [];
454
455        let dyn_slice = new_display_dyn_slice(&array);
456
457        assert_eq!(dyn_slice.len(), 0);
458        assert!(dyn_slice.is_empty());
459    }
460
461    #[test]
462    fn test_slice() {
463        let array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
464        let slice = partial_eq::new(&array);
465        assert_eq!(slice.len(), array.len());
466
467        // Slices equal to the original slice
468        let full_slices: [DynSlice<dyn PartialEq<i32>>; 6] = [
469            slice.slice(..).unwrap(),
470            slice.slice(0..).unwrap(),
471            slice.slice(..(array.len())).unwrap(),
472            #[allow(clippy::range_minus_one)]
473            slice.slice(..=(array.len() - 1)).unwrap(),
474            slice.slice(0..array.len()).unwrap(),
475            #[allow(clippy::range_minus_one)]
476            slice.slice(0..=(array.len() - 1)).unwrap(),
477        ];
478
479        for sub_slice in full_slices {
480            assert_eq!(sub_slice.metadata(), slice.metadata());
481            assert_eq!(sub_slice.len(), slice.len());
482            assert_eq!(sub_slice.as_ptr(), slice.as_ptr());
483        }
484
485        // Sub-slices bounded on one side
486        let sub_slice = slice.slice(2..).unwrap();
487        assert_eq!(sub_slice.metadata(), slice.metadata());
488        assert_eq!(sub_slice.len(), slice.len() - 2);
489        assert_eq!(sub_slice.as_ptr(), addr_of!(slice[2]).cast());
490
491        let sub_slice = slice.slice(..7).unwrap();
492        assert_eq!(sub_slice.metadata(), slice.metadata());
493        assert_eq!(sub_slice.len(), 7);
494        assert_eq!(sub_slice.as_ptr(), slice.as_ptr());
495
496        // Sub-slices bounded on both sides
497        let sub_slices = [
498            slice.slice(2..(array.len())).unwrap(),
499            #[allow(clippy::range_minus_one)]
500            slice.slice(2..=(array.len() - 1)).unwrap(),
501        ];
502
503        for sub_slice in sub_slices {
504            assert_eq!(sub_slice.metadata(), slice.metadata());
505            assert_eq!(sub_slice.len(), slice.len() - 2);
506            assert_eq!(sub_slice.as_ptr(), addr_of!(slice[2]).cast());
507        }
508
509        // Sub-slices with zero length
510        let zero_length_slices = [
511            slice.slice(0..0).unwrap(),
512            slice.slice(2..2).unwrap(),
513            #[allow(clippy::reversed_empty_ranges)]
514            slice.slice(2..=1).unwrap(),
515            slice.slice((array.len())..).unwrap(),
516        ];
517
518        for sub_slice in zero_length_slices {
519            assert_eq!(sub_slice.metadata(), slice.metadata());
520            assert_eq!(sub_slice.len(), 0);
521        }
522
523        // Invalid sub-slices
524        let invalid_slices = [
525            #[allow(clippy::range_plus_one)]
526            slice.slice(..(array.len() + 1)),
527            slice.slice(..=(array.len())),
528        ];
529
530        for sub_slice in invalid_slices {
531            assert!(sub_slice.is_none());
532        }
533    }
534
535    #[test]
536    #[should_panic(expected = "index out of bounds")]
537    fn index_empty() {
538        let slice = new_display_dyn_slice::<u8>(&[]);
539        println!("{}", &slice[0]);
540    }
541
542    #[test]
543    fn index() {
544        let slice = new_display_dyn_slice::<u8>(&[1, 2, 3, 4]);
545        assert_eq!(format!("{}", &slice[0]), "1");
546        assert_eq!(format!("{}", &slice[1]), "2");
547        assert_eq!(format!("{}", &slice[2]), "3");
548        assert_eq!(format!("{}", &slice[3]), "4");
549    }
550
551    #[test]
552    #[should_panic(expected = "index out of bounds")]
553    fn index_on_bound() {
554        let slice = new_display_dyn_slice::<u8>(&[1, 2, 3, 4]);
555        println!("{}", &slice[4]);
556    }
557
558    #[test]
559    #[should_panic(expected = "index out of bounds")]
560    fn index_out_of_bounds() {
561        let slice = new_display_dyn_slice::<u8>(&[1, 2, 3, 4]);
562        println!("{}", &slice[6]);
563    }
564}