#Why the compiler adds Sized bound by default?

23 messages · Page 1 of 1 (latest)

solid zodiac
#

after reading through this question #1141496871051870289 message I went on a quest to learn more about Unsized, and I have some questions:

What are the drawbacks of removing the Sized trait from a type?
e.g.: struct NewType<T: ?Sized>(T);

Is there any reason why I'd not remove this from every generic type I define?
I can still avoid adding ?Sized on the impl blocks if necessary, but just defining the type like this allows unsized coercions, with no cost at all.
(and why is this not the default behavior? if there is no harm in not adding Sized to every generic, why does the compiler adds it by default?)

Similarly, how does the std decides from what types to remove the Sized trait?
For example, why is Option defined over a generic sized T, while RwLock decides to explicitly removes it?

Before I read about unsized coercion and had a deeper understanding of this, I've expected something like:
let _: &Option<dyn Any> = &Some("test");
to work and I don't really see how it's harmful or any different from:
let _: &RwLock<dyn Any> = &RwLock::new("test");
which does work fine because it removes the default Sized trait

honest tangle
#
  1. There isn't any harm in adding ?Sized to definitions, no. I think it's not automated done because nobody thought about it tbh
  2. Option can't be ?Sized. Try writing one yourself, it's a compile error. While it would be possible to support it, it would require making certain guarantees about the layout of enums.

A type can only carry a ?Sized generic if it stores it behind a pointer OR it stores it as the last field.

prisma anchor
solid zodiac
solid zodiac
warped grotto
# solid zodiac I see, this makes a lot of sense. does any type that can reasonably remove the `...

Pretty much, yeah; the "holds a thing logically as a field" things (RefCell, RwLock, Mutex, ManuallyDrop), and the "holds/is a pointer to a thing" things (Box, Rc, Arc, raw pointers, references, NonNull, Ref(Mut), MutexGuard, RwLock(Read|Write)Guard)

The only thing I can thing of that hypothetically could support T: ?Sized but doesn't is MaybeUninit, because currently unions cannot have an unsized field in the language (yet).

solid zodiac
warped grotto
#

It would require some as-yet-undecided-and-unimplemented changes to the unstable semantics of unsized types to work properly IIUC.

#

E.g. how do you get the size of an MyUnsizedOption<[u8]> if it's None?
(I mean, I have a rough idea of how I think it should work, but IDK enough about the compiler to know if its reasonable)

#

Also: Option definitely could never support unsized types, since it would conflict with niche optimizations

solid zodiac
#

or is it a problem to reallocate when the variant of the option changes

warped grotto
# solid zodiac the size would be 0

Nope, that's not possible because if would have nowhere to put the discriminant.

Basically, you cannot directly create an unsozed type in most cases; it must be unsized from a sized type, e.g. the hypoythetical UnsizedOption<[u8]> would have to be unsized from a UnsizedOption<[u8; N]> for some array length N, and that N would determine the size of the value, even if it's None

warped grotto
solid zodiac
#

oh you coerce None of Option<[u8; N]> to it

#

ig it's not as useful as I imagined it'd be

warped grotto
#

I'm specifically not saying Option here btw, because Option has some niche opt guarantees that would be incompatible with my hypothetical semantics of unsized enums, e.g. Option<NonZeroU32> has no discriminant, but Option<dyn Debug> would have to know whether it is Some or None, and that would either have to 1. be included in the vtable (not gonna happen) 2. have a discriminant (incompatible with Option's niche guarantees)

So the hypothetical UnsizedOption<NonZeroU32> would be 8 bytes since it would have to have a discriminant to he able to coerce to UnsizedOption<dyn Debug> usefully.

solid zodiac
#

does option benefit from special optimizations that other enums don't?
will my enum Option<T> { None, Some(T) } be less efficient than option?

warped grotto
solid zodiac
#

It is further guaranteed that, for the cases above, one can mem::transmute from all valid values of T to Option<T> and from Some::<T>(_) to T (but transmuting None::<T> to T is undefined behaviour).
this is really powerful, thanks for sharing

wicked sluice
#

T gets a Sized bound by default because usually you’re using T by value. Either using it as a field in a struct or taking it by value in a function