T is Sync if and only if &T is Send?

Which one is reason and which one is the result?

It seems to say, if &T is Send, then T is Sync; totally confused.

Why is not: if T is Sync, then &T is Send. if T is not Sync, then &T never be Send?

"If and only if" means that both of these are true:

  • if &T is Send, then T is Sync
  • if T is Sync, then &T is Send

Therefore both of these are also true:

  • if T is not Sync, then &T is not Send
  • if &T is not Send, then T is not Sync

You can define Sync in terms of Send this way:

If &T is Send then T is Sync; otherwise, T is not Sync.

1 Like

The current implementation does have a causal direction:

unsafe impl<T: Sync + ?Sized> Send for &T {}

i.e the property of T influences the property of &T, and not vice-versa.

One could imagine it the other way though:

// core:
unsafe impl<T: ?Sized> Sync for T where &T: Send {}

// your crate:
unsafe impl Send for &YourType {}
1 Like

There is difference between the two.

unsafe impl<T: Sync + ?Sized> Send for &T {}

will not make YourType Sync when I write

unsafe impl Send for &YourType {}

But the later one does

Yes, they are different, and we can't change that, so the alternative is just a thought experiment. With the blanket Send for &T we have now, you're responsible for impl Sync for YourType. If we had chosen the other way with blanket Sync for T, you would write impl Send for &YourType instead.

Most of the time though, you don't even write these unsafe impl anyway if you're dealing with safe types, as they are automatically inferred.

I think this doesn’t actually work though, even without the blanket impl in place.

I’m not 100% sure though why that error exists. (Well, I do know that it’s pretty important in order to prevent something like impl Unpin for dyn Foo {}, but that reasoning really only applies to trait objects.)

I’m also a bit confused as to why the error message says cross-crate traits with a default impl while the error code description I linked doesn’t mention anything about that with a default impl part.

Interesting -- I wonder if that's actually an important restriction of auto traits overall, or just a limitation in how the compiler is currently able to reason about them?

In the world where we would be writing manual Send for &YourType, this also implies that we'd want it automatic most of the time, so for every type the compiler would have to automatically consider Send for YourType and Send for &YourType, the latter looking at each Send for &Field within YourType. And Sync wouldn't be an auto trait at all, just using its blanket impl where &T: Send. But from this angle, it does seem much cleaner that Send and Sync are separate auto traits rather than doubling the work for Send.

I recommend this post about the relationships between Send and Sync.

2 Likes