[][src]Crate thread_scoped_ref

Thread scoped reference

A library that is similar to a thread local storage but allows to store references / dyn Trait within a scope.

It's similar to std::thread_local but allows you to store non-static references. Since the reference is non-static, the value has to be scoped (the reference MUST NOT escape the scope). It also works with dynamic dispatch (e.g. dyn Trait). Scopes can be nested. Everything is thread-local.


Short example:

use thread_scoped_ref::{thread_scoped_ref, scoped, with};
use std::collections::HashMap;

thread_scoped_ref!(SOME_ENV_VALUES, HashMap<String, String>);

/// It's not possible to pass `&HashMap<String, String>` to this function since it's called
/// by some library you don't control...
fn read_env_value() {
  // ... so we read from the static 'SOME_ENV_VALUES'.
  with(&SOME_ENV_VALUES, |maybe_env_values| {
    // don't "unwrap" in reality: Since `maybe_env_values` will be `None` if not
    // called within a scope!
    let env_values = maybe_env_values.unwrap();
    assert_eq!("true", env_values.get("delete_entire_ssd").unwrap());

 /// An external library you don't control or generated code.
fn external_library(function_ptr : fn()) {

let mut env_values = HashMap::default();
env_values.insert("delete_entire_ssd".to_string(), "true".to_string());
// Create a scope. Note: We only need a reference to `env_values` (no move required).
scoped(&SOME_ENV_VALUES, &env_values, || {

Long example:

use thread_scoped_ref::{thread_scoped_ref, scoped, with};

/// Declare the `REF_TO_A_STRING`.
thread_scoped_ref!(REF_TO_A_STRING, str);

/// This function reads the value and prints the value.
fn value_consumer() {
  with(&REF_TO_A_STRING, |maybe_string| {
    // `maybe_string` is `Some` if this is called within a scope, or `None` if not called
    // within a scope.
    if let Some(string) = maybe_string {
      println!("String is: '{}'", string);
    } else {
      println!("There's no string.");

// Example #1: prints `There's no string` (since not called within a scope).

// Example #2: With a scope.
let my_string = "The String!".to_string();
// note: We use the reference and not the actual string. It's not static!
let my_string_ref = &my_string;
scoped(&REF_TO_A_STRING, my_string_ref, || {
  // prints `String is: 'The String!'`

// Example #3: Nested scopes.
let another_string = "Another string".to_string();
scoped(&REF_TO_A_STRING, &another_string, || {
  // prints `String is: 'Another string'`
  // a nested scope.
  scoped(&REF_TO_A_STRING, my_string_ref, || {
    // prints `String is: 'The String!'`
  // prints `String is: 'Another string'`

// Example #4: No scope (like example 1). prints `There's no string`.

Use case

It's useful if you need to 'inject' some sort of context into a function you provide that gets called by a library you don't control. One example is Serde: You can write custom serialize/deserialize methods but it's not possible to call them with custom data (a context) - unless you also write the serialization/deserialization of the container manually (not by using Serde derive).

Something like this can be achieved with thread scoped references. See the Serde demo test for details.



A shortcut macro for thread_local! { static IDENTIFIER : Scope<Type> = Scope::default() }.



A scope. There's usually one scope per thread.



Execute a function scoped with given value reference.


Gets the reference to value from the current scope. Given function will receive None if this is not called within a scope.