A trait to serve as documentation for pointer fattening?

After reading two SO questions which are confused on how DSTs interact with pointers, I wonder if it would be beneficial to have a trait that defines wether taking a pointer to a T results in a fat or a thin pointer. To some degree ptr::Pointee already does that, but it is a) Very much instable b) More so intended to allow types with custom ptr metadata c) Has no implementors

The last one is the most important IMO. I'd imagine having impl PointTo<Metadata=()> for T: Sized and impl PointTo<Metadata=<T as Pointee>::Metadata> for T: !Sized would be nice to have if only as documentation. The same way we can point to Deref when explaining what * does, we would be able to point at this proposed trait and explain that, "you see, since dyn Trait is not Sized, taking a reference to it includes some metadata in the resulting pointer". On the other hand, that might confuse some people into thinking that & is purely an operator, and isn't also a type constructor of its own, equivalent to Ref<_>.

It definitely seems like there should be a mention in the reference primitive docs.

Mitigating this somewhat — if you search & in rustdoc, it's aliased to show the primitive reference docs (as well as BitAnd).

A new trait would be unstable for a good period of time as well.

This isn't quite accurate. The Pointee trait is for the ptr::metadata APIs, and custom pointee kinds are still a long way off, if they're ever happening. (It does seem not unlikely that it'd work by implementing Pointee, though?)

This is more a rustdoc limitation than anything. Every type implements Pointee automatically as a compiler built-in, making custom implementations impossible.

Typical autotraits like Send/Sync are better supported and show up in the docs just fine. In theory it should be just a matter of deciding and communicating what pseudo implementations we want rustdoc to show.

We do also have the Thin trait alias for Pointee<Metadata=()>.

In the face of custom future pointee kinds, this isn't necessarily true. But changing it is difficult if not impossible. (At a bare minimum, T: Sized requires <T as Pointee>::Metadata: const Default, and it implies that the meta is non-critical, as ptr as usize as ptr is expected to work. Relaxing this would probably require adding Thin as a default bound opted out by ?Sized, such that ?Sized + Sized no longer a no-op.)

For a doc-only impl, it's not too big of a deal, but !Trait isn't a syntax which is available to stable Rust (yet), even for autotraits. We have ?Sized for unknown implementation, but !Trait for known not-implements doesn't exist yet.

If we're adding phantom impls for doc purposes, my set for current Rust would be

impl<T: Sized> Pointee for T {
    type Metadata = ();

impl<T> Pointee for [T] {
    type Metadata = usize;

impl<trait> Pointee for dyn trait {
    type Metadata = DynMetadata<Self>;

This isn't resilient to future custom Sized pointee kinds (but I expect these probably won't exist), but it is resilient to future unsized kinds (which is something current code is supposed to be resilient to).

All of the above said, I think ptr::metadata/Pointee should be handled the way mem::discriminant/marker::DiscriminantKind is — instead of exposing <T as DiscriminantKind>::Discriminant, code has to go through mem::Discriminant<T> instead.

1 Like