Layout of DST `Box`

Will there be any objections to define that the memory layout of DST Box is equal to that of a raw pointer (or reference) to the same DST? Alternatively, make Box::from_raw() a const fn. This is already guaranteed for T: Sized, but AFAIK not for unsized types.

Use case: I need to construct an empty Box<[T]> at compile time. Regarding dropping I am safe, because Box guarantees it won't deallocate zero-sized values (std::boxed - Rust):

For zero-sized values, the Box pointer has to be non-null and sufficiently aligned.

But I cannot transmute *const [T] to Box because I don't know if their layout is the same.

Guaranteeing layout equivalence prevents one potential design for allocators/storage generics, so is a harder sell than const from_raw, which only blocks that design on the ability to make trait functions const.

const Box::from_raw() (and Box::from_raw_in) doesn't need const traits, e.g. I can implement it for a look-alike on stable:

1 Like

It's fine for the A: Allocator design, it's the storages design which would require some const trait powers, due to the difference between pointers and handles.

At this point in time I don't think storage is very likely to be used, given the significant additional intrinsic complexity, but it hasn't been ruled out as an option yet, as far as I'm aware.

why can't from_raw_in just take a handle? that works fine:

OK, so what is the process to make something const? Do I need to open an ACP?

Having Box::from_raw be const looks like a major footgun. What happens when the box drops? It will try to deallocate the pointer, but a *mut T produced in a const context can't point to actual heap memory. Either it's a pointer to read-only memory, or some temporary generated during const evaluation.

Sure, from_raw is technically unsafe, so one could put on the user the burden that "the Box must never be dropped", but that's still a huge footgun. Even worse, I can't see any valid use for this API aside from creating a zero-sized slice like you do, You can't FFI in const contexts, you can't Drop in const contexts, you don't have any access to heap (even a temporary compile-time one).

Perhaps a better idea would be a specific Box::<[T]>::empty()? Also, why can't you use an empty vector?

2 Likes

It is already a precondition of Box::from_raw() that the pointer must've been allocated with the global allocator or a ZST, so there is nothing new here.

Introducing a method just for const contexts sounds like a bad idea.

Because Vec is three pointers in size and that's too large for me.

That's a non-answer.

It's just as usable outside outside of const contexts. I'm just not sure it's very useful in general, but why not?

1 Like

Why not? This affects memory usage negatively (yes, I've checked. This is in rust-analyzer, in case your are interested).

In non-const usage you can already do Box::new([]) or even Box::default().

I think there should be both const unsafe fn from_raw and something like const fn empty, since the latter is useful for when you want an empty boxed slice in const without unsafe and the former is useful for more unusual stuff e.g. you want an empty boxed slice with a particular address.

In actuality, const eval stops you from getting this far, and you will get a diagnostic like this:

error[E0493]: destructor of `Box<[T]>` cannot be evaluated at compile-time

Or some similar diagnostic about invalid provenance, etc.