with_thread_local/
lib.rs

1//! A micro crate that simplifies a bit the use of the std macro `thread_local!`.
2//!
3//! ```
4//! extern crate regex;
5//!
6//! use with_thread_local::with_thread_local;
7//! use regex::Regex;
8//!
9//! let user_input = "cat";
10//!
11//! let (is_a_pet, needs_a_walk) = with_thread_local! {
12//!     static REGEX_PET: Regex = Regex::new(r"cat|dog").unwrap();
13//!     static REGEX_WALK: Regex = Regex::new(r"dog").unwrap();
14//!
15//!     {
16//!         let is_a_pet = REGEX_PET.is_match(user_input);
17//!         let needs_a_walk = REGEX_WALK.is_match(user_input);
18//!
19//!         (is_a_pet, needs_a_walk)
20//!     }
21//! };
22//!
23//! assert!(is_a_pet && !needs_a_walk);
24//! ```
25//!
26//! You can also use its variant `move` to move variables inside the block. Though I admit I could
27//! not write a good example:
28//!
29//! ```
30//! extern crate regex;
31//!
32//! use with_thread_local::with_thread_local;
33//! use regex::Regex;
34//!
35//! let user_input = vec!["cat", "love", "dog"];
36//!
37//! let output = with_thread_local! {
38//!     static REGEX_PET: Regex = Regex::new(r"cat|dog").unwrap();
39//!
40//!     move {
41//!         user_input
42//!             .into_iter()
43//!             .filter(|s| REGEX_PET.is_match(s))
44//!             .collect::<Vec<_>>()
45//!     }
46//! };
47//!
48//! assert_eq!(output, ["cat", "dog"]);
49//! ```
50
51#[macro_export]
52macro_rules! with_thread_local {
53    (
54        $(
55        static $name:ident : $ty:ty = $init:expr;
56        )+
57
58        $block:block
59    ) => {{
60        thread_local! {
61            #[allow(unused_parens)]
62            static _THIS_LOCAL: ($($ty),+) = ($($init),+);
63        }
64
65        _THIS_LOCAL.with(|#[allow(non_snake_case, unused_parens)] ($($name),+)| $block)
66    }};
67    (
68        $(
69        static $name:ident : $ty:ty = $init:expr;
70        )+
71
72        move $block:block
73    ) => {{
74        thread_local! {
75            #[allow(unused_parens)]
76            static _THIS_LOCAL: ($($ty),+) = ($($init),+);
77        }
78
79        _THIS_LOCAL.with(move |#[allow(non_snake_case, unused_parens)] ($($name),+)| $block)
80    }};
81}
82
83#[cfg(test)]
84mod test {
85    #[test]
86    fn use_one() {
87        let res = with_thread_local! {
88            static ONE: usize = 42;
89
90            {
91                *ONE
92            }
93        };
94        assert_eq!(res, 42);
95    }
96
97    #[test]
98    fn use_two() {
99        let res = with_thread_local! {
100            static ONE: usize = 42;
101            static TWO: usize = 2;
102
103            {
104                *ONE + *TWO
105            }
106        };
107        assert_eq!(res, 44);
108    }
109}