Why is this type not automatially Unpin when I can trivally impl Unpin for it by hand

struct S<T> {
    t: T,
}

// Why is Unpin not automatically implemented for S when I can always
// (regardless of T) implement it manually like this?
impl<T> Unpin for S<T> {}
// By commenting out the previous line we can see that S is not automatically
// Unpin because the following code stops compiling.
fn check_unpin<T>() {
    fn use_unpin<T: Unpin>() {}
    use_unpin::<S<T>>();
}

If I can trivially make S Unpin then why is not automatically Unpin?
The Unpin documentation says

This trait is automatically implemented for almost every type.

without specifying the "almost" more.

I think the Unpin-ness of T does not matter as long we never construct a Pin<T>. Maybe the compiler is trying to protect me from accidentally making S !Unpin with a slight code change (that uses T in such a way).

To first order, implementing Unpin for a type is a promise that the type is not self-referencing. By implementing Unpin for S<T> you're effectively promising that S does not have structural pinning. If S<T> were automatically Unpin, it would be unsound to add a function like this one:

impl<T> S<T> {
    fn project(self: Pin<&mut Self>) -> Pin<&mut T> {
        /* use some `unsafe` in here */
    }
}

In order to add that function, you would have to un-implement Unpin first, which would be a backwards-incompatible change. It's more conservative to not implement Unpin for generic types unless you have consciously decided that S will not support structural pinning.

Language-feature-wise, Unpin is an auto trait, like Send and Sync (that explains the "almost every type").

Generally, a type being Unpin or not is completely irrelevant for types with no self: Pin<&mut Self> methods. It doesn't really enable you to do anything either way.

Of course, adding the unconditional impl makes some unsafe code incorrect, but still that situation only matters if you have a self: Pin<&mut Self> method, or may get one in the future.

2 Likes