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}