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
AlignedandMetaSizedas supertraits. - Object unsafe. (Logically has an associated
constfor 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
Sizedbound. - Object unsafe. (Logically has an associated
constfor 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
Sizedis. - Object safe. (Logically the provider of the size and align items in trait object vtables).
- In the standard library:
PhantomDatais relaxed fromT: ?Sized2021 toT: ?Sized2024‡.mem::align_ofis relaxed fromT: Sized2021 toT: ?Sized + Aligned2024‡mem::{size|align}_of_valare relaxed fromT: ?Sized2021 toT: ?Sized + MetaSized2024‡.?Sized2021 in positions where removing bounds is API compatible (i.e. generics that are never implied bounds downstream) become?Sized + MetaSized†.?Sized2021 in positions where removing bounds is API incompatible (i.e. trait associated types) are immediately relaxed to?Sized2024. (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
MetaSizedrequirements‡.
- Unsized fields are required to be
MetaSized. - For clarity, Rustdoc always renders docs using
?Sized + MetaAligned2024 independent of edition, never?Sized2021.[1]
- New builtin traits for capabilities of unsized types.
- edition ≤ 2021
- A
?Sizedunbound removes the defaultSizedandAlignedbounds but not theMetaSizedbound. - Unresolved: trait associated types, incl.
Self(currently aren't bound by default in generic contexts).
- A
- edition ≥ 2024
- A
?Sizedunbound removes both default bounds, allowing non-MetaSizedtypes to be used.
- A
- nightly‡
extern typeare notMetaSized.offset_of!only requires field types to beAlignedinstead ofSized.- Trait upcasting to
dyn MetaSizeddoes 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+ MetaSizedfor 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
wherebounds onMetaSizedprobably shouldn't preventdyn Trait + MetaSizedfrom being object safe, even thoughdyn Traitwouldn't be.[2] - Default trait method bodies have an implicit
Self: Trait + ?Sizedgeneric. How should that be handled for an edition ≤ 2021 trait; is it a supertrait bound so edition ≥ 2024 is required to provideMetaSizedfor 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 ExternTypebeing a giant footgun. - Make
mem::{size|align}_of_valpanic 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
?MetaSizedand 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.
MetaSizedbounds in both, but a missingMetaSizedbound 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.
Sizeddoesn't strictly need to implyAligned.[3]- More traits for more exotic layout, e.g. splitting
MetaAlignedfromMetaSized, orDynSizedwhich is allowed to read from the pointer to compute size/align (e.g. to callstrlenfor&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_valdoing 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 typemight finally have a path to stabilization.- Relaxing more APIs to accept non-
MetaSizedtypes. - Allowing users to (unsafely) implement the layout traits to communicate properties about external types.
Some sort of indicator for
?Sized2024 might be desirable, to distinguish it from?Sized2021. Always Rendering it as?MetaSizedmakes 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-MetaSizedtypes. ↩︎A separate thing I'd potentially like to see for edition 2024 is
dyn traititem declarations which ensure the trait is object safe. Interesting combination of concepts here: it's inferred object safe or not for+ MetaSizedonly by default, butdyn traitmakes it object safe without the need forMetaSized? ↩︎Has a fun implication then that
?Sized + Sizedis not the same as the lack of?Sized, since it unboundsAligned.) ↩︎