Struct LocalKey

1.0.0 · Source
pub struct LocalKey<T>
where T: 'static,
{ inner: fn(Option<&mut Option<T>>) -> *const T, }
Expand description

A thread local storage (TLS) key which owns its contents.

This key uses the fastest possible implementation available to it for 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 and Destruction

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 panics, the whole process will be aborted.

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.

§Single-thread Synchronization

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>>.

§Examples

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);

§Platform-specific behavior

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:

  1. On Unix systems when pthread-based TLS is being used, destructors will not be run for TLS values on the main thread when it exits. Note that the application will exit immediately after the main thread exits as well.
  2. On all platforms it’s possible for TLS to re-initialize other TLS slots during destruction. Some platforms ensure that this cannot happen infinitely by preventing re-initialization of any slot that has been destroyed, but not all platforms have this guard. Those platforms that do not guard typically have a synthetic limit after which point no more destructors are run.
  3. When the process exits on Windows systems, TLS destructors may only be run on the thread that causes the process to exit. This is because the other threads may be forcibly terminated.

§Synchronization in thread-local destructors

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.

Fields§

§inner: fn(Option<&mut Option<T>>) -> *const T

Implementations§

Source§

impl<T> LocalKey<T>
where T: 'static,

1.0.0 · Source

pub fn with<F, R>(&'static self, f: F) -> R
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.

§Panics

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.

§Examples
thread_local! {
    pub static STATIC: String = String::from("I am");
}

assert_eq!(
    STATIC.with(|original_value| format!("{original_value} initialized")),
    "I am initialized",
);
1.26.0 · Source

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.

§Panics

This function will still panic!() if the key is uninitialized and the key’s initializer panics.

§Examples
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")),
);
Source§

impl<T> LocalKey<Cell<T>>
where T: 'static,

1.73.0 · Source

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

Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.

§Examples
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);
1.73.0 · Source

pub fn get(&'static self) -> T
where 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

Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.

§Examples
use std::cell::Cell;

thread_local! {
    static X: Cell<i32> = const { Cell::new(1) };
}

assert_eq!(X.get(), 1);
1.73.0 · Source

pub fn take(&'static self) -> T
where 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

Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.

§Examples
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);
1.73.0 · Source

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

Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.

§Examples
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);
Source

pub fn update(&'static self, f: impl FnOnce(T) -> T)
where T: Copy,

🔬This is a nightly-only experimental API. (local_key_cell_update)

Updates the contained value using a function.

§Examples
#![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);
Source§

impl<T> LocalKey<RefCell<T>>
where T: 'static,

1.73.0 · Source

pub fn with_borrow<F, R>(&'static self, f: F) -> R
where 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

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.

§Examples
use std::cell::RefCell;

thread_local! {
    static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}

X.with_borrow(|v| assert!(v.is_empty()));
1.73.0 · Source

pub fn with_borrow_mut<F, R>(&'static self, f: F) -> R
where 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

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.

§Examples
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]));
1.73.0 · Source

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

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.

§Examples
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]));
1.73.0 · Source

pub fn take(&'static self) -> T
where 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

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.

§Examples
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()));
1.73.0 · Source

pub fn replace(&'static self, value: T) -> T

Replaces the contained value, returning the old value.

§Panics

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.

§Examples
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]));

Trait Implementations§

1.16.0 · Source§

impl<T> Debug for LocalKey<T>
where T: 'static,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

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>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Conv for T

§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
§

impl<T> FmtForward for T

§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

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> Pipe for T
where T: ?Sized,

§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> Tap for T

§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
§

impl<T> TryConv for T

§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more