Make type Sync/Send/!Sync/!Send

I have problem with this task. I need to make this types appropriate to their names, but have problems with it.

    struct OnlySync(PhantomData<()>);
    impl OnlySync {
        fn print(&self, val: i32) {
            println!("OnlySync - {val}")
        }
    }

    struct OnlySend(PhantomData<Cell<i32>>);
    impl OnlySend {
        fn print(&self, val: i32) {
            println!("OnlySend - {val}")
        }
    }

    struct SyncAndSend(PhantomData<i32>);
    impl SyncAndSend {
        fn print(&self, val: i32) {
            println!("SyncAndSend - {val}")
        }
    }

    struct NotSyncNotSend(PhantomData<Rc<i32>>);
    impl NotSyncNotSend {
        fn print(&self, val: i32) {
            println!("NotSyncNotSend - {val}")
        }
    }

This is a very odd homework problem, and I'd be suspicious of any code which uses PhantomData for types which don't use unsafe in their implementations.

Sync without Send is a relatively rare combination, but it can be found, for example for MutexGuard because some OS mutexes require you to unlock them from the same thread they were locked on (which in turn restricts where the destructor of MutexGuard may be executed[1]; but otherwise, besides its destructor, MutexGuard is thread safe[2]).


  1. hence you cannot send it to other thread ↩︎

  2. besides its destructor onlocking the Mutex, it is a simple mutable reference, and thus entirely thread-safe, so Sync is fine ↩︎

1 Like

Generally the best way to manually control what autotraits are present when needbe is to opt out of all autotraits and conditionally reimplement the autotraits that you want. So if you want Sync but not Send, opt out of both then unsafe impl Sync.

My task to do this without unsafe at all. I have problem with OnlySync type, others should be fine.

Another example of Sync + !Send type is std::sync::Exclusive<*mut ()>. It is always Sync because it doesn't expose shared references to the wrapped value, so it can always be shared with other threads (it will just be useless), while its Sendness depend on the wrapped type (and *mut () is not Send). This is an unstable API though.

As I hinted at above, something like PhantomData<MutexGuard<'static, ()>> should do the job.

1 Like

If your task is about only using PhantomData for this, then the task is reduced to finding any existing type that already has the properties you want. std docs show Send/Sync in auto trait implementations.

But normally, I think it'd be more common to use unsafe impl Sync|Send where it's needed to add these properties.

To "reset" a type to have neither Sync nor Send you can use a raw pointer. In most cases that raw pointer is directly used (for FFI, or clever memory management). But PhantomData<*const ()> should work too (if your type is a handle, or a zero-sized special singleton for generics, or something similarly abstract).

Thanks everyone. Solution that got accepted:

 struct OnlySync(PhantomData<MutexGuard<'static, ()>>);
    impl OnlySync {
        fn print(&self, val: i32) {
            println!("OnlySync - {val}")
        }
    }

    struct OnlySend(PhantomData<UnsafeCell<i32>>);
    impl OnlySend {
        fn print(&self, val: i32) {
            println!("OnlySend - {val}")
        }
    }

    struct SyncAndSend(PhantomData<i32>);
    impl SyncAndSend {
        fn print(&self, val: i32) {
            println!("SyncAndSend - {val}")
        }
    }

    struct NotSyncNotSend(PhantomData<Rc<i32>>);
    impl NotSyncNotSend {
        fn print(&self, val: i32) {
            println!("NotSyncNotSend - {val}")
        }
    }

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.