Trait with different associated types for sized and unsized types?

I'm trying to create a trait similar to the following:

trait MaybeSized {
    type Sized;
}

The idea is that MaybeSized is implemented for all types. For sized types, Self::Sized = Self, and for unsized types, Self::Sized = (). I need to implement this without using any unstable features (ie, I assume this is doable using the specialization feature).

Any ideas for how to accomplish this?

I don’t think this is possible.

Feel free to share your use-case, maybe there are ways to avoid the need for this trait and trait impl.

The use case is that I'm working on a utility to support field projection inside of #[repr(transparent)] types, which is backed by this trait:

unsafe trait Projectable {
    type Inner: ?Sized;
    type Wrapped<T>;

    fn get_inner(&self) -> *const Self::Inner;
    fn get_inner_mut(&mut self) -> *mut Self::Inner;
}

I'd like to support container types which support wrapping unsized types, such as:

#[repr(transparent)]
struct Wrapper<T: ?Sized>(T);

If I want to do that and also support projecting into unsized fields, I'd need to change the associated type definition of Projectable::Wrapped<T> to Projectable::Wrapped<T: ?Sized>. However, that would mean that I couldn't implement Projectable for types which don't support wrapping unsized types. E.g.:

unsafe impl<T> Projectable for MaybeUninit<T> {
    type Inner = T;

    // ERROR: `MaybeUninit` requires `U: Sized`
    type Wrapped<U: ?Sized> = MaybeUninit<U>;

    fn get_inner(&self) -> *const Self::Inner { ... }
    fn get_inner_mut(&mut self) -> *mut Self::Inner { ... }
}

My thinking was that, if I could get the proposed MaybeSized trait working, I could instead do:

type Wrapped<U: ?Sized> = MaybeUninit<<U as MaybeSized>::Sized>;

It might seem weird to say that "wrapping an unsized type results in MaybeUninit<()>", but the idea is that, since MaybeUninit doesn't support unsized types, there's no way you could ever try to field project into an unsized type - if the outer type is sized, then all of its fields must be sized too. So this is just a way of making the compiler happy; it'd never actually get used in practice.

I suppose another approach could be to modify Wrapped to be something like:

type Wrapped<T: ?Sized> where /* bound that represents that this type supports wrapping `T` */

For wrapper types which support wrapping unsized inner types, this where bound would be empty (ie, would be satisfied for all T: ?Sized), and for wrapper types which don't, it would be T: Sized. Obviously you can't literally write that - there's no such thing as an "associated where bound" - but I wonder if you could come up with something that has the same effect.

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.