Why do methods on thread::LocalKey take &'static self?

Every single inherent method on LocalKey takes &'static self as the receiver. My question is, why does it require the reference be 'static? I can't seem to find any explanation in the docs, or anywhere else. None of the methods hold on to the reference after they return, either. All they do is de-reference it to call its function pointer.

if val {
    THREAD_LOCAL_1
} else {
    THREAD_LOCAL_2
}.with(|_| /* etc */ );

For example, this code produces error[E0716]: temporary value dropped while borrowed.

It's trivial to get a 'static reference to a thread_local! static, so this isn't much of a problem; I'm just wondering why the bound was put there in the first place.

I don't know precisely why the API is designed as such, but you can work around this by just taking a reference to them -- the error you're encountering is because you're trying to move these thread locals, not take reference to them:

if val {
    &THREAD_LOCAL_1
} else {
    &THREAD_LOCAL_2
}.with(|_| /* etc */ );

Which should be fine since references to statics should be 'static to begin with.

1 Like

I do know you can just take a reference to them. I wanted to know if there's a reason the API requires specifically a 'static reference.
Also, you can absolutely use a LocalKey by value. The thread_local! macro puts it in a const, not a static, so this code compiles without problem:

thread_local!{ static VAR: Vec<i32> = vec![1, 2, 3] }
fn main() {
    let x = VAR;
    let y = VAR;
}

Just a guess, but the initial design had a method that returned a Ref<T: 'static>: Deref<Target = T>, basically a lifetime-less pointer analogous to a &'static T.[1] And there were at least lingering hopes of reviving that API somehow.

You could check that guess (and vitality of the hopes) by submitting a PR to loosen the signature I suppose.


  1. That was pre-leakpocalypse but it would be trivial to get a &'static T out of such a Ref<T> today. ↩ī¸Ž

3 Likes

Good point, a PR would probably be the best way to find out more info.

Though, I just tested it, and I think it's actually too late to change the API. It would break any code that relied on the bound for type inference.

About the comment you linked, I also came across it while looking into this, but I think I disagree with your guess. The current implementation of LocalKey doesn't own anything; It's basically just a fancy 'static reference already. (I also don't think that can be changed in the future without breakage either.)

pub struct LocalKey<T: 'static> {
    // just a plain old function pointer
    inner: fn(Option<&mut Option<T>>) -> *const T,
}

They wouldn't need the 'static bound on the method receiver even if they gave out Ref's, since it's just a function pointer. All that matters is that it lives long enough to be called, right?

Nonetheless, thanks for the advice, I at least now know why the API hasn't been changed since Rust 1.0.

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.