Consistency of `impl<X> Trait for X`

Why is it that this code behaves the way it does?

Why doesn't impl<X> Printer for X cover every possible type including dyn Unsized ? Is there some implicit filtering of what <X> covers?

I'd expect an error on line 10 to be what it is when you uncomment the specialization at lines 25-29 that is an error indicating that X is not Sized therefore core::mem::size_of::<X>() wouldn't work on every X. But it seems somehow Rust decides to only take X such where X: Sized implicitly?

Is this behavior documented?

See ?Sized.

Yes, the Sized bound. It requires the <X : ?Sized> unbound to have a truly generic type.

Ah I see, so there isn't a way to do impl<X> Trait for X such that X is both sized and unsized, correct? I have to write two separate implementation blocks.

Makes sense, I just didn't know about the implicit : Sized on all types.

Generic parameter like impl<T> or struct Foo<T> implies T: Sized, otherwise one cannot store it in variable, receive it as function parameters, or return it.

In the early days of Rust language development the core team found that in most case user expects behaviors listed above that requires Sized bound. So they added : Sized as a implicit bound to allow to return them from methods by default.

?Sized does not mean !Sized; it means that Sized is not a requirement (i.e., Sized is optional). Thus a single impl<X: ?Sized> Trait for X can be written when X: Sized is not an inherent requirement.

2 Likes

I see, out of interest is it possible to do "Unsized" then? Say I wanted to add a trait/impl for any type that does not have a size. I'm just exploring the language/runtime and playing around with the more nuanced things looking for consistency issues etc.

My understanding is that !Sized would be a negative trait bound, which is not supported.

It would require the unstable specialization feature; for instance:

#![feature(specialization)]

trait AmISized {
    fn am_i_sized (self: &'_ Self) -> bool;
}
impl<T : ?Sized> AmISized for T {
    default
    fn am_i_sized (self: &'_ Self) -> bool { false }
}
impl<T> AmISized for T {
    fn am_i_sized (self: &'_ Self) -> bool { true }
}