I poked around a while looking for documentation about this but no luck, the whole API is presented as fait accompli. Why does LocalKey
, used as part of the thread_local!
macro, use with()
to expose its wrapped value instead of a guard or even a Deref
implementation like most other synchronization mechanisms in std?
Since it's &'static LocalKey<T>
you can get &'static T
if it impls Deref
. But TLS values don't exist after the thread is terminated so it can't be &'static
That's the same problem as Mutex<T>
though, which is why it uses a local MutexGuard<T>
that implements Deref<T>
.
Perhaps it's that forget()
ing a thread-local guard would be unsafe?
That's probably it. Closure based guards are needed when leaking some type isn't safe. A leaked reference guard stops anything else from taking the lock, but dead lock isn't memory unsafe by Rust standards.
No that's not the same problem. In case you have &'static Mutex<T>
you can obtain MutexGuard<'static, T>
. This is not true for LocalKey
. The reason Mutex
uses a guard is to release the lock.
Ah, that's tricky!
Another contributing factor is how/when thread-local values are destroyed.
Normally, the OS will call destructor for a thread-local variable whenever your thread exits, however imagine I created a thread-local object (e.g. a logger) where the destructor accesses another thread-local object (e.g. imagine our logger writes messages to a thread-local buffer before sending them somewhere). If thread-locals were implemented as a 'static
RAII guard, it would let you get a &'static T
reference that can be stored in the logger, then if the buffer was destroyed before the logger, our logger would be left with a dangling reference and we'd have a bad time.
Using a callback that references to thread-locals can't "escape" from doesn't suffer from this issue.
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.