I have a thread_local string and I want to return a reference to it. Looks like I just can't do this, because the compiler does not know that info is actually static.
|
6 | TEST_INFO.with(|info| {
| ----- return type of closure is Ref<'2, str>
| |
| has type `&'1 RefCell<Option<String>>`
7 | Ref::map(info.borrow(), |info| info.as_ref().unwrap() as &str)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
error: could not compile `playground` due to previous error
This is not allowed as there is no way to guarantee that the reference will not outlive the current thread otherwise. You will have to pass a closure to get which accepts as argument a reference to the string.
Even within the same thread you have a problem if you manage to sneak the reference into another thread local that gets destructured after the original thread local you took a reference from.
You can use Rc<str> (slightly thinner Rc<String>) if you don't need to mutate it, otherwise you'll need Rc<RefCell<…>> and return a refcounted clone of that.
To help other readers avoid confusion trying to understand this explanation: I would use “dropped” instead of “destructured”, as “destructuring” is something different…
Thread locals (unlike global / ordinary statics) are only alive for the time the thread is running, and when the thread finishes will be dropped. So their destructors are run. (The order of those destructors seems to be not specified AFAICT.) Now, if one thread local’s value could contain a reference (or a way to obtain a reference infallibly) to another thread local, then – depending on their drop order – it could happen, than the destructor of the first thread local has, through that reference value, access to the memory of another thread local that has already been dropped.
Of course, there’s nothing preventing other thread locals to be accessed in a destructor, and in fact some possible negative effects are mentioned in the docs, though those effects are safe: e.g. the accessed thread-local could be re-initialized, or a panic could occur. But this is only about accessing the thread-local through its local key, any direct access to another thread-local without a step that might perform re-initialization or panicking if needed would be unsound.