Allowing Deref without exposing that &T is Send?

As I understand it what it means for T to be Sync is that &T is Send.

Say I have some wrapper NotSync<T>. The idea is that it will act like a T that is !Sync even if T is, so you can be sure the object won't be shared between threads. Now of course if you want to actually call some of T's methods It will be necessary for the wrapper to implement Deref. But that exposes access to an unwrapped &T, which defeats the point of the wrapper. Is there any way to do something like this generically?

If you don't care about being generic, instead of implementing Deref you can manually write a bunch of wrapper methods that call methods on the wrapped object. But obviously that's less convenient.

I agree with all of the observations you already made, e.g. about the fact that Deref defeats the purpose of preventing Sync or that for concrete types one could just “manually write a bunch of wrapper methods”.

you talk about being generic — well in general, the methods of T could “expose” references to T themselves directly, so it’s not reasonably to be completely generic. Perhaps a generic approach could be tailored around types T that implement a fixed/known trait, but even then I’m having a hard time imagining what kind of safety guarantees you are going to achieve in any sort of general setting. For a concrete, fixed/known type T, the “manually write a bunch of wrapper methods” approach may work, but – at least in case that you control the implementation of T yourself – I would wonder why T was Sync in the first place if you care so much about the fact that it isn’t sync in certain settings. I guess the other way works better; if you provide a wrapper Sync<T> or even just another type SyncT that is handed out in situations where sharing between different threads is, for some reason, known to be safe safe. That wrapper or new type could also go the Deref route then, I guess.

What I’m saying, or perhaps asking, is: Do you have some concrete use case? This sounds like a potential XY problem situation IMO, and even if it isn’t I would be curious what your imagined application or general motivation is behind this question.

Ah, good point.

Your instinct was right. As my understanding of the language has been getting better I've been thinking again about what would be necessary for thread locals to be more ergonomic -- not requiring all uses be wrapped inside a closure. When I asked this question I was mistakenly thinking that hiding the Syncness of T was somehow one of the things the closure accomplished, but then I realized the closure still receives a normal reference and in fact you could spawn a scoped thread inside a LocalKey::with block. It's just that it works out because &T is still a normal pointer to the unique location with that specific thread's instance. The thread specific segment register logic happens in constructing the &T in the first place, so by the time you get inside the closure the thread specific lookup part is over.