pub struct LocalKey<T: 'static> { /* private fields */ }
A thread local storage (TLS) key which owns its contents.
This key uses the fastest implementation available on the target platform. It is instantiated with the thread_local! macro and the primary method is the with method, though there are helpers to make working with Cell types easier.
The with method yields a reference to the contained value which cannot outlive the current thread or escape the given closure.
Initialization is dynamically performed on the first call to a setter (e.g. with) within a thread, and values that implement Drop get destructed when a thread exits. Some platform-specific caveats apply, which are explained below. Note that, should the destructor panic, the whole process will be aborted. On platforms where initialization requires memory allocation, this is performed directly through System, allowing the global allocator to make use of thread local storage.
A LocalKey’s initializer cannot recursively depend on itself. Using a LocalKey in this way may cause panics, aborts, or infinite recursion on the first call to with.
Though there is no potential race with other threads, it is still possible to obtain multiple references to the thread-local data in different places on the call stack. For this reason, only shared (&T) references may be obtained.
To allow obtaining an exclusive mutable reference (&mut T), typically a Cell or RefCell is used (see the std::cell for more information on how exactly this works). To make this easier there are specialized implementations for LocalKey<Cell<T>> and LocalKey<RefCell<T>>.
use std::cell::Cell;
use std::thread;
// explicit `const {}` block enables more efficient initialization
thread_local!(static FOO: Cell<u32> = const { Cell::new(1) });
assert_eq!(FOO.get(), 1);
FOO.set(2);
// each thread starts out with the initial value of 1
let t = thread::spawn(move || {
assert_eq!(FOO.get(), 1);
FOO.set(3);
});
// wait for the thread to complete and bail out on panic
t.join().unwrap();
// we retain our original value of 2 despite the child thread
assert_eq!(FOO.get(), 2);Note that a “best effort” is made to ensure that destructors for types stored in thread local storage are run, but not all platforms can guarantee that destructors will be run for all types in thread local storage. For example, there are a number of known caveats where destructors are not run:
On Windows, synchronization operations (such as JoinHandle::join) in thread local destructors are prone to deadlocks and so should be avoided. This is because the loader lock is held while a destructor is run. The lock is acquired whenever a thread starts or exits or when a DLL is loaded or unloaded. Therefore these events are blocked for as long as a thread local destructor is running.
impl<T: 'static> LocalKey<T>
pub fn with<F, R>(&'static self, f: F) -> Rwhere
F: FnOnce(&T) -> R,Acquires a reference to the value in this TLS key.
This will lazily initialize the value if this thread has not referenced this key yet.
This function will panic!() if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
thread_local! {
pub static STATIC: String = String::from("I am");
}
assert_eq!(
STATIC.with(|original_value| format!("{original_value} initialized")),
"I am initialized",
);pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>where
F: FnOnce(&T) -> R,Acquires a reference to the value in this TLS key.
This will lazily initialize the value if this thread has not referenced this key yet. If the key has been destroyed (which may happen if this is called in a destructor), this function will return an AccessError.
This function will still panic!() if the key is uninitialized and the key’s initializer panics.
thread_local! {
pub static STATIC: String = String::from("I am");
}
assert_eq!(
STATIC.try_with(|original_value| format!("{original_value} initialized")),
Ok(String::from("I am initialized")),
);impl<T: 'static> LocalKey<Cell<T>>
pub fn set(&'static self, value: T)
Sets or initializes the contained value.
Unlike the other methods, this will not run the lazy initializer of the thread local. Instead, it will be directly initialized with the given value if it wasn’t initialized yet.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::Cell;
thread_local! {
static X: Cell<i32> = panic!("!");
}
// Calling X.get() here would result in a panic.
X.set(123); // But X.set() is fine, as it skips the initializer above.
assert_eq!(X.get(), 123);pub fn get(&'static self) -> Twhere
T: Copy,Returns a copy of the contained value.
This will lazily initialize the value if this thread has not referenced this key yet.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::Cell;
thread_local! {
static X: Cell<i32> = const { Cell::new(1) };
}
assert_eq!(X.get(), 1);pub fn take(&'static self) -> Twhere
T: Default,Takes the contained value, leaving Default::default() in its place.
This will lazily initialize the value if this thread has not referenced this key yet.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::Cell;
thread_local! {
static X: Cell<Option<i32>> = const { Cell::new(Some(1)) };
}
assert_eq!(X.take(), Some(1));
assert_eq!(X.take(), None);pub fn replace(&'static self, value: T) -> T
Replaces the contained value, returning the old value.
This will lazily initialize the value if this thread has not referenced this key yet.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::Cell;
thread_local! {
static X: Cell<i32> = const { Cell::new(1) };
}
assert_eq!(X.replace(2), 1);
assert_eq!(X.replace(3), 2);pub fn update(&'static self, f: impl FnOnce(T) -> T)where
T: Copy,local_key_cell_update #143989)
Updates the contained value using a function.
#![feature(local_key_cell_update)]
use std::cell::Cell;
thread_local! {
static X: Cell<i32> = const { Cell::new(5) };
}
X.update(|x| x + 1);
assert_eq!(X.get(), 6);impl<T: 'static> LocalKey<RefCell<T>>
pub fn with_borrow<F, R>(&'static self, f: F) -> Rwhere
F: FnOnce(&T) -> R,Acquires a reference to the contained value.
This will lazily initialize the value if this thread has not referenced this key yet.
Panics if the value is currently mutably borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
X.with_borrow(|v| assert!(v.is_empty()));pub fn with_borrow_mut<F, R>(&'static self, f: F) -> Rwhere
F: FnOnce(&mut T) -> R,Acquires a mutable reference to the contained value.
This will lazily initialize the value if this thread has not referenced this key yet.
Panics if the value is currently borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
X.with_borrow_mut(|v| v.push(1));
X.with_borrow(|v| assert_eq!(*v, vec![1]));pub fn set(&'static self, value: T)
Sets or initializes the contained value.
Unlike the other methods, this will not run the lazy initializer of the thread local. Instead, it will be directly initialized with the given value if it wasn’t initialized yet.
Panics if the value is currently borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = panic!("!");
}
// Calling X.with() here would result in a panic.
X.set(vec![1, 2, 3]); // But X.set() is fine, as it skips the initializer above.
X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));pub fn take(&'static self) -> Twhere
T: Default,Takes the contained value, leaving Default::default() in its place.
This will lazily initialize the value if this thread has not referenced this key yet.
Panics if the value is currently borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
X.with_borrow_mut(|v| v.push(1));
let a = X.take();
assert_eq!(a, vec![1]);
X.with_borrow(|v| assert!(v.is_empty()));pub fn replace(&'static self, value: T) -> T
Replaces the contained value, returning the old value.
Panics if the value is currently borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
let prev = X.replace(vec![1, 2, 3]);
assert!(prev.is_empty());
X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));impl<T: 'static> Debug for LocalKey<T>
impl<T> Freeze for LocalKey<T>
impl<T> RefUnwindSafe for LocalKey<T>
impl<T> Send for LocalKey<T>
impl<T> Sync for LocalKey<T>
impl<T> Unpin for LocalKey<T>
impl<T> UnwindSafe for LocalKey<T>
impl<T> Any for Twhere
T: 'static + ?Sized,impl<T> Borrow<T> for Twhere
T: ?Sized,impl<T> BorrowMut<T> for Twhere
T: ?Sized,impl<T> From<T> for T
fn from(t: T) -> T
Returns the argument unchanged.
impl<T, U> Into<U> for Twhere
U: From<T>,fn into(self) -> U
Calls U::from(self).
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
impl<T, U> TryFrom<U> for Twhere
U: Into<T>,type Error = Infallible
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
impl<T, U> TryInto<U> for Twhere
U: TryFrom<T>,
© 2010 The Rust Project Developers
Licensed under the Apache License, Version 2.0 or the MIT license, at your option.
https://doc.rust-lang.org/std/thread/struct.LocalKey.html