Getting a type's `Layout` from its `Pointee::Metadata`

Background: I am working on a project related to custom DST, and to construct them, I need their layout. So far, I have been able to figure out how to get the Layout for each category of DST as well as Sized type (the only required information being its Pointee::Metadata):

fn sized_layout<T: Sized>(_metadata: <T as Pointee>::Metadata) -> Layout {
    Layout::new::<T>()
}
fn slice_layout<T>(metadata: <[T] as Pointee>::Metadata) -> Layout {
    Layout::array::<T>(metadata).unwrap()
}
fn str_layout(metadata: <str as Pointee>::Metadata) -> Layout {
    slice_layout::<u8>(metadata)
}
fn dyn_layout<Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>>(
    metadata: <Dyn as Pointee>::Metadata,
) -> Layout {
    metadata.layout()
}

Ideally, I would combine the implementations into a trait, and this would be the interface:

trait LayoutFromMetadata: Pointee {
    fn layout_from_metadata(metadata: Self::Metadata) -> Layout;
}

However, I can't get them to work generically as a trait because of how I've implemented dyn_layout. The ?Sized bound make it overlap with the implementation for all the previous one and refused by the compiler.

The question is: is there a better way to get a generic type's Layout? I only need to support the 3 built-in DST, but it would be great if extensible, so custom DST can also be supported.

Your implementations do not actually overlap thanks to them requiring incompatible associated bounds, however the compiler currently does not use this for checking overlaps, leading to your error. The current fix for this is to write a separate helper trait implemented for that associated type, and then write a single impl for your trait where the associated type is bounded by that helper trait.

4 Likes

Thanks, that's very helpful! Though you did forget a ?Sized in LayoutFromMetadata's blanket implementation :stuck_out_tongue:

Is there a reason you didn't implement the helper trait for str? Is the layout actually undefined?

Oops, I just forgot about it.

1 Like

Something like this should be good:

#![feature(layout_for_ptr)]
#![feature(ptr_metadata)]

use core::alloc::Layout;
use core::ptr::Pointee;

fn layout_from_metadata<T: ?Sized>(metadata: <T as Pointee>::Metadata) -> Layout {
    unsafe {
        Layout::for_value_raw(std::ptr::from_raw_parts::<T>(
            std::ptr::null::<()>(),
            metadata,
        ))
    }
}

Although, I’d have to do some digging whether or not for_raw_value is supposed to stay unsafe and if yes, what conditions one would need to actually check here.

I am not aware of any way the above layout_from_metadata would currently actually be unsound though.

Edit: Actually, I’m noticing some requirements w.r.t. overflow or sized larger than isize::MAX in the safety docs, too; those aren’t addressed above – but also they should be a non-issue in all use-cases where you’re actually planning to use this Layout.

Also see the tracking issue for the unstable feature for more context.

Edit2: Yeah… usage like layout_from_metadata::<[u8]>(usize::MAX) is reported by Miri as UB.

This is probably a better solution than rolling out my own trait.

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.