Implementation of MinSlice - Creating a custom DST

Today I was reading the the blog post It's Time to Get Hyped About Const Generics in Rust, which contains an interesting type MinSlice, which can be used to assert that a slice has at least const N elements.
Looking at the implementation, I found the following (normal comments mine):

use std::slice;

// From: https://git.nora.codes/nora/nslice/src/branch/main/src/lib.rs

pub struct MinSlice<T, const N: usize> {
    /// The bounded region of memory. Exactly `N` `T`s.
    // Sized Type
    pub head: [T; N],
    /// Zero or more remaining `T`s after the `N` in the bounded region.
    // DST
    pub tail: [T],
}


impl<T, const N: usize> MinSlice<T, N> {

    /// Produce a `&MinSlice` from a slice of `T`s without checking its length.
    ///
    /// # Safety
    ///
    /// The caller is responsible for upholding the length invariant
    /// `slice.len() >= N`, in addition to all normal slice invariants.
    pub unsafe fn from_slice_unchecked(slice: &[T]) -> &MinSlice<T, N> {
        let resized = slice::from_raw_parts(
            slice.as_ptr(), 
            slice.len() - N     // Substruct size of head array from slice                     
        );
        &*(                             // deref and borrow to get reference
                                        // to MinSlice 
            resized as *const [T]       // convert slice reference (fat pointer) 
                                        // to raw fat pointer to slice
            as *const MinSlice<T, N>    // convert fat raw pointer to slice 
                                        // to fat raw pointer to MinSlice 
        )
    }
}

Playground

I'm a little bit confused by how the from_slice_unchecked method works and why (or if?) it is safe to call, assuming slice.len() >= N.
Specifically, I'm wondering the following:

  • the resized slice has a length of slice.len() - N, why is this information propagated as the length of tail when coercing the slice to a fat pointer to MinSlice?
  • the Rustonomicon has a similar example, where it is stated, that an unsizing coercion for a generic type is the only supported way of creating such a type. From my understanding, this is not the case in the MinSlice implementation. Is this coercion also supported? If yes, is this documented?
  • Isn't the layout of MinSlice unspecified, meaning there could potentially be padding between head and tail?

I looked at the nomicon's DST and Unsized coercions documentation, but they didn't answer my questions (or I didn't completely understand the information presented there :sweat_smile: ).

That's how these nested DSTs work; the metadata of the container DST is the metadata of the field DST. E.g. RFC 2580.

The couched it with "properly supported". Perhaps it could be rephrased as "possible without unsafe". I think they're more referring to CoerceUnsized being unstable.

Also note that there is no coercion to the custom DST going on in the example; it's a pointer-to-pointer cast.

Without a repr, technically so (I'm unaware of any guarantee of not adding superfluous padding).

1 Like

I thought of one other thing; though the metadata is definitely the same between a container and the field, I don't think the layout of their wide pointers is technically guaranteed to be the same.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.