There's no way that actual support for exotically sized types is landing in time for the 2024 edition. But I think we understand enough about the problem space that we can reserve the space for them in the 2024 edition. The goal is to make it so we can compatibly enable the use of unsized types in the language and standard library without allowing them to be provided to code not written to be aware of them.
...although after writing this out I think this actually ends up being most of the support work, and less is deferrable than I initially thought could be. Namely, due to associated types no longer getting MetaSized
bounds and requiring manual bounds for them (potentially infinitely)
I'm still posting this as a useful exploration of the minimal support reservation, but I'm thinking just implementing RFC#3396 Extern types v2 in full is easier and preferable. At the least, my list of alternatives is potentially useful (especially the "bodge" option to reduce edition difference).
The necessary changes amount to roughly:
- All editions
- New builtin traits for capabilities of unsized types.
Sized
— has a statically known size and alignment.- The existing trait, newly gaining
Aligned
andMetaSized
as supertraits. - Object unsafe. (Logically has an associated
const
for the size.)
- The existing trait, newly gaining
Aligned
‡ — has a statically known alignment.- An empty† marker trait not implementable† by user code.
- Implied by the default
Sized
bound. - Object unsafe. (Logically has an associated
const
for the alignment.)
MetaSized
— has a size and alignment known from pointer metadata.- An empty† marker trait not implementable† by user code.
- Is a default bound on generics and associated types identically to but independently from how
Sized
is. - Object safe. (Logically the provider of the size and align items in trait object vtables).
- In the standard library:
PhantomData
is relaxed fromT: ?Sized
2021 toT: ?Sized
2024‡.mem::align_of
is relaxed fromT: Sized
2021 toT: ?Sized + Aligned
2024‡mem::{size|align}_of_val
are relaxed fromT: ?Sized
2021 toT: ?Sized + MetaSized
2024‡.?Sized
2021 in positions where removing bounds is API compatible (i.e. generics that are never implied bounds downstream) become?Sized + MetaSized
†.?Sized
2021 in positions where removing bounds is API incompatible (i.e. trait associated types) are immediately relaxed to?Sized
2024. (Compatibility is maintained via the edition ≤ 2021 changes.)- Fix insufficient bounds errors introduced by the previous two guidelines, by adding bounds to generic type projections† or removing
MetaSized
requirements‡.
- Unsized fields are required to be
MetaSized
. - For clarity, Rustdoc always renders docs using
?Sized + MetaAligned
2024 independent of edition, never?Sized
2021.[1]
- New builtin traits for capabilities of unsized types.
- edition ≤ 2021
- A
?Sized
unbound removes the defaultSized
andAligned
bounds but not theMetaSized
bound. - Unresolved: trait associated types, incl.
Self
(currently aren't bound by default in generic contexts).
- A
- edition ≥ 2024
- A
?Sized
unbound removes both default bounds, allowing non-MetaSized
types to be used.
- A
- nightly‡
extern type
are notMetaSized
.offset_of!
only requires field types to beAligned
instead ofSized
.- Trait upcasting to
dyn MetaSized
does not have a unique vtable. It uses the prefix of whatever vtable it was upcast from. - (certainly missed other relevancies)
†: Minimizes the immediately effective change, but intended to be weakened in the future.
‡: More than minimally necessary, but included here for relevance.
Unresolved questions:
- Exact behavior of
dyn Trait
. Implicit+ MetaSized
for edition ≤ 2021 for backwards compatibility, not for edition ≥ 2024 for extern type support, yeah, but the change is more relevant in this position than for generics, since owning a trait object effectively requires allocation, thusMetaSized
. - Method
where
bounds onMetaSized
probably shouldn't preventdyn Trait + MetaSized
from being object safe, even thoughdyn Trait
wouldn't be.[2] - Default trait method bodies have an implicit
Self: Trait + ?Sized
generic. How should that be handled for an edition ≤ 2021 trait; is it a supertrait bound so edition ≥ 2024 is required to provideMetaSized
for impls? And whatever the solution is, it needs to be object safe. - Associated types don't have any implied bounds currently, but if we want to prevent edition ≤ 2021 code from getting a non-
MetaSized
I previously made most of a (just the current edition) implementation of MetaSized
, but got stuck on expectations for Self
(in trait default method bodies) and associated types.
Alternatives:
- Do nothing, and delay support language support for exotically (un)sized types until edition 2027 or later.
- Do nothing, but pick some compromise that allows exotically (un)sized types to exist before edition 2027:
- Live with external types claiming a 1ZST layout and
&mut ExternType
being a giant footgun. - Make
mem::{size|align}_of_val
panic for external types. (Runtime instead of compile-time checking.) - Forbid external types from ever appearing in generics. (Essentially this proposal, but only relaxing references and their builtin deref to support
?MetaSized
and nothing else.)
- Live with external types claiming a 1ZST layout and
- Bodge support in the current edition, but proper in the next — same behavior w.r.t.
MetaSized
bounds in both, but a missingMetaSized
bound in the current editions is only a warning, with asking for the layout panicking.- Future editions still have to soundly handle that asking for type layout could panic for provided types, but new code should always request/provide witness that it can't.
Sized
doesn't strictly need to implyAligned
.[3]- More traits for more exotic layout, e.g. splitting
MetaAligned
fromMetaSized
, orDynSized
which is allowed to read from the pointer to compute size/align (e.g. to callstrlen
for&CStr
, or to fetch a vtable from behind the reference e.g. for nonfinal cxx classes).- I've personally come to the conclusion that these are more trouble than they're worth, even if
Box<Dst>
being thin is nice. size_of_val
doing a read is a pointer provenance validity nightmare;UnsafeCell
- A read to determine the alignment (
DynAligned
) is borderline unusable; you have to store the pointer to the type directly, since determine the alignment you need the data pointer, but to offset to the field you need to know the alignment. - Plus, you can make
Thin<Pointer<Dst>>
work just fine without language support beyond splitting data pointer and metadata. You can even already do it on stable, if you limit yourself to just slice tailed DSTs; this is what my crates erasable and slice-dst do together.
- I've personally come to the conclusion that these are more trouble than they're worth, even if
Future extensions:
extern type
might finally have a path to stabilization.- Relaxing more APIs to accept non-
MetaSized
types. - Allowing users to (unsafely) implement the layout traits to communicate properties about external types.
Some sort of indicator for
?Sized
2024 might be desirable, to distinguish it from?Sized
2021. Always Rendering it as?MetaSized
makes sense, but I don't think requiring writing that in edition ≥ 2024 is necessary. But it is how edition ≤ 2021 would be extended to be able to be generic over non-MetaSized
types. ↩︎A separate thing I'd potentially like to see for edition 2024 is
dyn trait
item declarations which ensure the trait is object safe. Interesting combination of concepts here: it's inferred object safe or not for+ MetaSized
only by default, butdyn trait
makes it object safe without the need forMetaSized
? ↩︎Has a fun implication then that
?Sized + Sized
is not the same as the lack of?Sized
, since it unboundsAligned
.) ↩︎