scoped_tls_hkt/
lib.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7//! Scoped thread-local storage
8//!
9//! This module provides the ability to generate *scoped* thread-local
10//! variables. In this sense, scoped indicates that thread local storage
11//! actually stores a reference to a value, and this reference is only placed
12//! in storage for a scoped amount of time.
13//!
14//! There are no restrictions on what types can be placed into a scoped
15//! variable, but all scoped variables are initialized to the equivalent of
16//! null. Scoped thread local storage is useful when a value is present for a known
17//! period of time and it is not required to relinquish ownership of the
18//! contents.
19//!
20//! # Examples
21//!
22//! ## Basic usage
23//!
24//! ```
25//! use scoped_tls_hkt::scoped_thread_local;
26//!
27//! scoped_thread_local!(static FOO: u32);
28//!
29//! # fn main() {
30//! // Initially each scoped slot is empty.
31//! assert!(!FOO.is_set());
32//!
33//! // When inserting a value, the value is only in place for the duration
34//! // of the closure specified.
35//! FOO.set(&1, || {
36//!     FOO.with(|slot| {
37//!         assert_eq!(*slot, 1);
38//!     });
39//! });
40//! # }
41//! ```
42//!
43//! ## Mutable value
44//!
45//! ```
46//! use scoped_tls_hkt::scoped_thread_local;
47//!
48//! scoped_thread_local!(static mut FOO: u32);
49//!
50//! # fn main() {
51//! // Initially each scoped slot is empty.
52//! assert!(!FOO.is_set());
53//!
54//! // When inserting a value, the value is only in place for the duration
55//! // of the closure specified.
56//! let mut x = 1;
57//! FOO.set(&mut x, || {
58//!     FOO.with(|slot| {
59//!         assert_eq!(*slot, 1);
60//!
61//!         // We can mutate the value
62//!         *slot = 42;
63//!     });
64//! });
65//!
66//! // Changes will be visible externally
67//! assert_eq!(x, 42);
68//! # }
69//! ```
70//!
71//! ## Higher-kinded types
72//!
73//! ```
74//! use scoped_tls_hkt::scoped_thread_local;
75//!
76//! // Must implement Copy
77//! #[derive(Copy, Clone)]
78//! struct Foo<'a> {
79//!     x: &'a str, // Lifetime is covariant
80//!     y: i32,
81//! }
82//!
83//! scoped_thread_local!(static FOO: for<'a> Foo<'a>);
84//!
85//! # fn main() {
86//! // Initially each scoped slot is empty.
87//! assert!(!FOO.is_set());
88//!
89//! // When inserting a value, the value is only in place for the duration
90//! // of the closure specified.
91//! FOO.set(Foo { x: "Hello", y: 42 }, || {
92//!     FOO.with(|slot| {
93//!         assert_eq!(slot.x, "Hello");
94//!         assert_eq!(slot.y, 42);
95//!     });
96//! });
97//! # }
98//! ```
99//!
100//! ## Mutable higher-kinded types
101//!
102//! For mutable HKTs, the types must implement the [`ReborrowMut`](ReborrowMut)
103//! trait, and the `Result` associated type should be the `Self` type, but with
104//! the lifetime substituted with the trait's lifetime parameter.
105//!
106//! The [`ReborrowMut`](ReborrowMut) trait is implemented automatically for
107//! many built-in types, including primitive types, references, mutable
108//! references and tuples (up to length 10). Where this is insufficient, you
109//! can implement the trait yourself: doing so should not require any unsafe
110//! code.
111//!
112//! ```
113//! use scoped_tls_hkt::scoped_thread_local;
114//!
115//! scoped_thread_local!(static mut FOO: for<'a> (&'a mut i32, &'a mut f32));
116//!
117//! # fn main() {
118//! // Initially each scoped slot is empty.
119//! assert!(!FOO.is_set());
120//!
121//! // References to local variables can be stored.
122//! let mut x = 1;
123//! let mut y = 2.0;
124//! FOO.set((&mut x, &mut y), || {
125//!     FOO.with(|(u, v)| {
126//!         assert_eq!(*u, 1);
127//!         assert_eq!(*v, 2.0);
128//!         *u = 42;
129//!     });
130//! });
131//!
132//! assert_eq!(x, 42);
133//! # }
134//! ```
135
136#![deny(missing_docs, warnings)]
137
138use std::cell::Cell;
139use std::thread::LocalKey;
140
141/// Trait representing the act of "reborrowing" a mutable reference
142/// to produce a new one with a shorter lifetime.
143pub trait ReborrowMut<'a> {
144    /// Type of the shorter reference
145    type Result;
146
147    /// Produces a new reference with lifetime 'a
148    fn reborrow_mut(&'a mut self) -> Self::Result;
149}
150
151impl<'a, 'b: 'a, T: ?Sized> ReborrowMut<'a> for &'b mut T {
152    type Result = &'a mut T;
153    fn reborrow_mut(&'a mut self) -> Self::Result {
154        &mut **self
155    }
156}
157
158impl<'a, 'b: 'a, T: ?Sized> ReborrowMut<'a> for &'b T {
159    type Result = &'a T;
160    fn reborrow_mut(&'a mut self) -> Self::Result {
161        &**self
162    }
163}
164
165macro_rules! define_tuple_reborrow {
166    (@expand $($t:ident),*) => {
167        impl<'a, $($t,)*> ReborrowMut<'a> for ($($t,)*)
168        where
169            $($t: ReborrowMut<'a> + 'a),*
170        {
171            type Result = ($($t::Result,)*);
172            #[allow(clippy::unused_unit)]
173            fn reborrow_mut(&'a mut self) -> Self::Result {
174                #[allow(non_snake_case)]
175                let ($($t,)*) = self;
176                ($($t.reborrow_mut(),)*)
177            }
178        }
179    };
180    () => {
181        define_tuple_reborrow!(@expand);
182    };
183    ($t:ident $(, $ts:ident)*) => {
184        define_tuple_reborrow!(@expand $t $(, $ts)*);
185        define_tuple_reborrow!($($ts),*);
186    };
187}
188
189macro_rules! define_copy_reborrow {
190    ($($t:ty,)*) => {
191        $(
192            impl<'a> ReborrowMut<'a> for $t {
193                type Result = $t;
194                fn reborrow_mut(&'a mut self) -> Self::Result {
195                    *self
196                }
197            }
198        )*
199    }
200}
201
202define_tuple_reborrow!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
203define_copy_reborrow! {
204    bool, char, isize, usize,
205    i8, u8, i16, u16, i32, u32, i64, u64, i128, u128,
206    f32, f64,
207    std::any::TypeId,
208}
209
210/// The macro. See the module level documentation for the description and examples.
211#[macro_export]
212macro_rules! scoped_thread_local {
213    ($(#[$attrs:meta])* $vis:vis static $name:ident: $(#[$tattrs:meta])* for<$lt:lifetime> $ty:ty) => (
214        $(#[$tattrs])*
215        #[allow(non_camel_case_types)]
216        $vis struct $name<$lt> where ::std::cell::Cell<::std::option::Option<$ty>>: 'static {
217            inner: &$lt ::std::thread::LocalKey<::std::cell::Cell<::std::option::Option<$ty>>>,
218        }
219        const _:() = {
220            type Hkt<$lt> = $ty;
221            use ::std::cell::Cell;
222            use ::std::option::Option;
223            use ::std::marker::Sync;
224            use ::std::ops::{FnOnce, Drop};
225            use ::std::thread::LocalKey;
226
227            unsafe impl Sync for $name<'static> {}
228
229            unsafe fn cast_to_static(x: Hkt<'_>) -> Hkt<'static> {
230                std::mem::transmute(x)
231            }
232
233            // This wrapper helps to ensure that the 'static lifetime is not visible
234            // to the safe code.
235            fn cast_from_static<'a, 'b>(x: &'a Hkt<'static>) -> Hkt<'b> where 'a: 'b {
236                *x
237            }
238
239            impl $name<'static> {
240                pub fn set<F, R>(&'static self, t: Hkt<'_>, f: F) -> R
241                    where F: FnOnce() -> R
242                {
243                    struct Reset {
244                        key: &'static LocalKey<Cell<Option<Hkt<'static>>>>,
245                        val: Option<Hkt<'static>>,
246                    }
247                    impl Drop for Reset {
248                        fn drop(&mut self) {
249                            self.key.with(|c| c.set(self.val.take()));
250                        }
251                    }
252                    let prev = self.inner.with(|c| {
253                        // Safety: we are only changing the lifetime. We enforce the
254                        // lifetime constraints via the `Reset` struct.
255                        c.replace(Some(unsafe { cast_to_static(t) }))
256                    });
257                    let _reset = Reset { key: self.inner, val: prev };
258                    f()
259                }
260
261                pub fn with<F, R>(&'static self, f: F) -> R
262                    where F: FnOnce(Hkt<'_>) -> R
263                {
264                    let val = self.inner.with(|c| c.get());
265                    let val = val.expect("cannot access a scoped thread local variable without calling `set` first");
266
267                    // This also asserts that Hkt is covariant
268                    f(cast_from_static(&val))
269                }
270
271                /// Test whether this TLS key has been `set` for the current thread.
272                pub fn is_set(&'static self) -> bool {
273                    self.inner.with(|c| c.get().is_some())
274                }
275            }
276        };
277
278        $(#[$attrs])*
279        $vis static $name: $name<'static> = {
280            type Hkt<$lt> = $ty;
281            use ::std::cell::Cell;
282            use ::std::option::Option;
283
284            thread_local!(static FOO: Cell<Option<Hkt<'static>>> = {
285                Cell::new(None)
286            });
287
288            $name {
289                inner: &FOO,
290            }
291        };
292    );
293    ($(#[$attrs:meta])* $vis:vis static mut $name:ident: $(#[$tattrs:meta])* for<$lt:lifetime> $ty:ty) => (
294        $(#[$tattrs])*
295        #[allow(non_camel_case_types)]
296        $vis struct $name<$lt> where ::std::cell::Cell<::std::option::Option<$ty>>: 'static {
297            inner: &$lt ::std::thread::LocalKey<::std::cell::Cell<::std::option::Option<$ty>>>,
298        }
299        const _:() = {
300            type Hkt<$lt> = $ty;
301
302            use ::std::cell::Cell;
303            use ::std::option::Option;
304            use ::std::marker::Sync;
305            use ::std::ops::{FnOnce, Drop};
306            use ::std::thread::LocalKey;
307
308            use $crate::ReborrowMut;
309
310            unsafe impl Sync for $name<'static> {}
311
312            unsafe fn cast_to_static(x: Hkt<'_>) -> Hkt<'static> {
313                std::mem::transmute(x)
314            }
315
316            // This wrapper helps to ensure that the 'static lifetime is not visible
317            // to the safe code.
318            fn cast_from_static<'is_reborrow_mut_general_enough, 'a, 'b>(x: &'a mut Hkt<'is_reborrow_mut_general_enough>) -> Hkt<'b> where 'a: 'b {
319                //let y: &'b mut Hkt<'_> = unsafe { std::mem::transmute(x) };
320                <Hkt<'is_reborrow_mut_general_enough> as ReborrowMut<'_>>::reborrow_mut(x)
321            }
322
323            impl $name<'static> {
324                fn replace<F, R>(&'static self, value: Option<Hkt<'_>>, f: F) -> R
325                    where F: FnOnce(Option<Hkt<'_>>) -> R
326                {
327                    struct Reset {
328                        key: &'static LocalKey<Cell<Option<Hkt<'static>>>>,
329                        val: Option<Hkt<'static>>,
330                    }
331                    impl Drop for Reset {
332                        fn drop(&mut self) {
333                            self.key.with(|c| c.set(self.val.take()));
334                        }
335                    }
336                    let prev = self.inner.with(move |c| {
337                        // Safety: we are only changing the lifetime. We enforce the
338                        // lifetime constraints via the `Reset` struct.
339                        c.replace(value.map(|x| unsafe { cast_to_static(x) }))
340                    });
341                    let mut reset = Reset { key: self.inner, val: prev };
342                    f(reset.val.as_mut().map(cast_from_static))
343                }
344
345                /// Inserts a value into this scoped thread local storage slot for a
346                /// duration of a closure.
347                pub fn set<F, R>(&'static self, t: Hkt<'_>, f: F) -> R
348                    where F: FnOnce() -> R
349                {
350                    self.replace(Some(t), |_| f())
351                }
352
353                /// Gets a value out of this scoped variable.
354                ///
355                /// This function takes a closure which receives the value of this
356                /// variable. For the duration of the closure, the key will appear
357                /// unset.
358                ///
359                /// # Panics
360                ///
361                /// This function will panic if `set` has not previously been called,
362                /// or if the call is nested inside another (multiple mutable borrows
363                /// of the same value are not allowed).
364                ///
365                pub fn with<F, R>(&'static self, f: F) -> R
366                    where F: FnOnce(Hkt<'_>) -> R
367                {
368                    self.replace(None, |val| f(val.expect("cannot access a scoped thread local variable without calling `set` first")))
369                }
370
371                /// Test whether this TLS key has been `set` for the current thread.
372                pub fn is_set(&'static self) -> bool {
373                    self.replace(None, |prev| prev.is_some())
374                }
375
376            }
377        };
378        $(#[$attrs])*
379        $vis static $name: $name<'static> = {
380            type Hkt<$lt> = $ty;
381            use ::std::cell::Cell;
382            use ::std::option::Option;
383            thread_local!(static FOO: Cell<Option<Hkt<'static>>> = {
384                Cell::new(None)
385            });
386
387            $name {
388                inner: &FOO,
389            }
390        };
391    );
392    ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
393        $(#[$attrs])*
394        $vis static $name: $crate::ScopedKey<$ty> = $crate::ScopedKey {
395            inner: {
396                thread_local!(static FOO: ::std::cell::Cell<::std::option::Option<&'static $ty>> = {
397                    ::std::cell::Cell::new(None)
398                });
399                &FOO
400            },
401        };
402    );
403    ($(#[$attrs:meta])* $vis:vis static mut $name:ident: $ty:ty) => (
404        $(#[$attrs])*
405        $vis static $name: $crate::ScopedKeyMut<$ty> = $crate::ScopedKeyMut {
406            inner: {
407                thread_local!(static FOO: ::std::cell::Cell<::std::option::Option<&'static mut $ty>> = {
408                    ::std::cell::Cell::new(None)
409                });
410                &FOO
411            },
412        };
413    );
414}
415
416/// Type representing a thread local storage key corresponding to a reference
417/// to the type parameter `T`.
418///
419/// Keys are statically allocated and can contain a reference to an instance of
420/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
421/// and `with`, both of which currently use closures to control the scope of
422/// their contents.
423pub struct ScopedKey<T: ?Sized + 'static> {
424    #[doc(hidden)]
425    pub inner: &'static LocalKey<Cell<Option<&'static T>>>,
426}
427
428unsafe impl<T: ?Sized + 'static> Sync for ScopedKey<T> {}
429
430unsafe fn cast_to_static<T: ?Sized + 'static>(x: &T) -> &'static T {
431    std::mem::transmute(x)
432}
433
434// This wrapper helps to ensure that the 'static lifetime is not visible
435// to the safe code.
436fn cast_from_static<'a, 'b, T: ?Sized + 'static>(x: &'a &T) -> &'b T
437where
438    'a: 'b,
439{
440    x
441}
442
443impl<T: ?Sized + 'static> ScopedKey<T> {
444    /// Inserts a value into this scoped thread local storage slot for a
445    /// duration of a closure.
446    ///
447    /// While `cb` is running, the value `t` will be returned by `get` unless
448    /// this function is called recursively inside of `cb`.
449    ///
450    /// Upon return, this function will restore the previous value, if any
451    /// was available.
452    ///
453    /// # Examples
454    ///
455    /// ```
456    /// use scoped_tls_hkt::scoped_thread_local;
457    ///
458    /// scoped_thread_local!(static FOO: u32);
459    ///
460    /// # fn main() {
461    /// FOO.set(&100, || {
462    ///     let val = FOO.with(|v| *v);
463    ///     assert_eq!(val, 100);
464    ///
465    ///     // set can be called recursively
466    ///     FOO.set(&101, || {
467    ///         // ...
468    ///     });
469    ///
470    ///     // Recursive calls restore the previous value.
471    ///     let val = FOO.with(|v| *v);
472    ///     assert_eq!(val, 100);
473    /// });
474    /// # }
475    /// ```
476    pub fn set<F, R>(&'static self, t: &T, f: F) -> R
477    where
478        F: FnOnce() -> R,
479    {
480        struct Reset<T: ?Sized + 'static> {
481            key: &'static LocalKey<Cell<Option<&'static T>>>,
482            val: Option<&'static T>,
483        }
484        impl<T: ?Sized + 'static> Drop for Reset<T> {
485            fn drop(&mut self) {
486                self.key.with(|c| c.set(self.val));
487            }
488        }
489        let prev = self.inner.with(|c| {
490            // Safety: we are only changing the lifetime. We enforce the
491            // lifetime constraints via the `Reset` struct.
492            c.replace(Some(unsafe { cast_to_static(t) }))
493        });
494        let _reset = Reset {
495            key: self.inner,
496            val: prev,
497        };
498        f()
499    }
500
501    /// Gets a value out of this scoped variable.
502    ///
503    /// This function takes a closure which receives the value of this
504    /// variable.
505    ///
506    /// # Panics
507    ///
508    /// This function will panic if `set` has not previously been called.
509    ///
510    /// # Examples
511    ///
512    /// ```no_run
513    /// use scoped_tls_hkt::scoped_thread_local;
514    ///
515    /// scoped_thread_local!(static FOO: u32);
516    ///
517    /// # fn main() {
518    /// FOO.with(|slot| {
519    ///     // work with `slot`
520    /// # drop(slot);
521    /// });
522    /// # }
523    /// ```
524    pub fn with<F, R>(&'static self, f: F) -> R
525    where
526        F: FnOnce(&T) -> R,
527    {
528        let val = self
529            .inner
530            .with(|c| c.get())
531            .expect("cannot access a scoped thread local variable without calling `set` first");
532        f(cast_from_static(&val))
533    }
534
535    /// Test whether this TLS key has been `set` for the current thread.
536    pub fn is_set(&'static self) -> bool {
537        self.inner.with(|c| c.get().is_some())
538    }
539}
540
541/// Type representing a thread local storage key corresponding to a mutable reference
542/// to the type parameter `T`.
543///
544/// Keys are statically allocated and can contain a reference to an instance of
545/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
546/// and `with`, both of which currently use closures to control the scope of
547/// their contents.
548///
549/// This differs from a `ScopedKey` because it provides access through a mutable
550/// reference. As a result, when the `with(..)` method is used to access the value,
551/// the key will appear unset whilst the closure is running. This is to prevent
552/// the value being borrowed a second time.
553pub struct ScopedKeyMut<T: ?Sized + 'static> {
554    #[doc(hidden)]
555    pub inner: &'static LocalKey<Cell<Option<&'static mut T>>>,
556}
557
558unsafe impl<T: ?Sized + 'static> Sync for ScopedKeyMut<T> {}
559
560unsafe fn cast_to_static_mut<T: ?Sized + 'static>(x: &mut T) -> &'static mut T {
561    std::mem::transmute(x)
562}
563
564// This wrapper helps to ensure that the 'static lifetime is not visible
565// to the safe code.
566fn cast_from_static_mut<'a, 'b, T: ?Sized + 'static>(x: &'a mut &mut T) -> &'b mut T
567where
568    'a: 'b,
569{
570    x
571}
572
573impl<T: ?Sized + 'static> ScopedKeyMut<T> {
574    fn replace<F, R>(&'static self, t: Option<&mut T>, f: F) -> R
575    where
576        F: FnOnce(Option<&mut T>) -> R,
577    {
578        struct Reset<T: ?Sized + 'static> {
579            key: &'static LocalKey<Cell<Option<&'static mut T>>>,
580            val: Option<&'static mut T>,
581        }
582        impl<T: ?Sized + 'static> Drop for Reset<T> {
583            fn drop(&mut self) {
584                self.key.with(|c| c.set(self.val.take()));
585            }
586        }
587        let prev = self.inner.with(move |c| {
588            // Safety: we are only changing the lifetime. We enforce the
589            // lifetime constraints via the `Reset` struct.
590            c.replace(t.map(|x| unsafe { cast_to_static_mut(x) }))
591        });
592        let mut reset = Reset {
593            key: self.inner,
594            val: prev,
595        };
596        f(reset.val.as_mut().map(cast_from_static_mut))
597    }
598
599    /// Inserts a value into this scoped thread local storage slot for a
600    /// duration of a closure.
601    pub fn set<F, R>(&'static self, t: &mut T, f: F) -> R
602    where
603        F: FnOnce() -> R,
604    {
605        self.replace(Some(t), |_| f())
606    }
607
608    /// Gets a value out of this scoped variable.
609    ///
610    /// This function takes a closure which receives the value of this
611    /// variable. For the duration of the closure, the key will appear
612    /// unset.
613    ///
614    /// # Panics
615    ///
616    /// This function will panic if `set` has not previously been called,
617    /// or if the call is nested inside another (multiple mutable borrows
618    /// of the same value are not allowed).
619    ///
620    pub fn with<F, R>(&'static self, f: F) -> R
621    where
622        F: FnOnce(&mut T) -> R,
623    {
624        self.replace(None, |val| {
625            f(val
626                .expect("cannot access a scoped thread local variable without calling `set` first"))
627        })
628    }
629
630    /// Test whether this TLS key has been `set` for the current thread.
631    pub fn is_set(&'static self) -> bool {
632        self.replace(None, |prev| prev.is_some())
633    }
634}
635
636#[cfg(test)]
637mod tests {
638    use std::cell::Cell;
639    use std::panic;
640    use std::sync::mpsc::{channel, Sender};
641    use std::thread;
642
643    scoped_thread_local!(static FOO: u32);
644
645    #[test]
646    fn smoke() {
647        scoped_thread_local!(static BAR: u32);
648
649        assert!(!BAR.is_set());
650        BAR.set(&1, || {
651            assert!(BAR.is_set());
652            BAR.with(|slot| {
653                assert_eq!(*slot, 1);
654            });
655        });
656        assert!(!BAR.is_set());
657    }
658
659    #[test]
660    fn cell_allowed() {
661        scoped_thread_local!(static BAR: Cell<u32>);
662
663        BAR.set(&Cell::new(1), || {
664            BAR.with(|slot| {
665                assert_eq!(slot.get(), 1);
666            });
667        });
668    }
669
670    #[test]
671    fn scope_item_allowed() {
672        assert!(!FOO.is_set());
673        FOO.set(&1, || {
674            assert!(FOO.is_set());
675            FOO.with(|slot| {
676                assert_eq!(*slot, 1);
677            });
678        });
679        assert!(!FOO.is_set());
680    }
681
682    #[test]
683    #[cfg_attr(miri, ignore)]
684    fn panic_resets() {
685        struct Check(Sender<u32>);
686        impl Drop for Check {
687            fn drop(&mut self) {
688                FOO.with(|r| {
689                    self.0.send(*r).unwrap();
690                })
691            }
692        }
693
694        let (tx, rx) = channel();
695
696        // Temporarily suppress panic output, as it would interfere
697        // with the test harness output.
698        let prev_hook = panic::take_hook();
699        panic::set_hook(Box::new(|_| {
700            // Do nothing
701        }));
702
703        let t = thread::spawn(|| {
704            FOO.set(&1, || {
705                let _r = Check(tx);
706
707                FOO.set(&2, || panic!());
708            });
709        });
710
711        let res = t.join();
712        panic::set_hook(prev_hook);
713
714        assert_eq!(rx.recv().unwrap(), 1);
715        assert!(res.is_err());
716    }
717
718    #[test]
719    fn attrs_allowed() {
720        scoped_thread_local!(
721            /// Docs
722            static BAZ: u32
723        );
724
725        scoped_thread_local!(
726            #[allow(non_upper_case_globals)]
727            static quux: u32
728        );
729
730        let _ = BAZ;
731        let _ = quux;
732    }
733
734    #[test]
735    fn hkt_struct() {
736        #[derive(Copy, Clone)]
737        pub struct Foo<'a> {
738            x: &'a str,
739            y: &'a i32,
740        }
741        scoped_thread_local!(static BAR: for<'a> Foo<'a>);
742
743        assert!(!BAR.is_set());
744        BAR.set(Foo { x: "hi", y: &1 }, || {
745            assert!(BAR.is_set());
746            BAR.with(|slot| {
747                assert_eq!(slot.x, "hi");
748                assert_eq!(slot.y, &1);
749            });
750        });
751        assert!(!BAR.is_set());
752    }
753
754    #[test]
755    fn hkt_trait() {
756        scoped_thread_local!(static BAR: for<'a> &'a dyn std::fmt::Display);
757
758        assert!(!BAR.is_set());
759        BAR.set(&"Hello", || {
760            assert!(BAR.is_set());
761            BAR.with(|slot| {
762                assert_eq!(slot.to_string(), "Hello");
763            });
764            BAR.set(&42, || {
765                assert!(BAR.is_set());
766                BAR.with(|slot| {
767                    assert_eq!(slot.to_string(), "42");
768                });
769            });
770        });
771        assert!(!BAR.is_set());
772    }
773
774    #[test]
775    fn mut_value() {
776        scoped_thread_local!(static mut BAR: i32);
777
778        assert!(!BAR.is_set());
779        let mut x = 0;
780
781        BAR.set(&mut x, || {
782            assert!(BAR.is_set());
783            BAR.with(|slot| {
784                assert!(!BAR.is_set());
785                assert_eq!(*slot, 0);
786                *slot = 42;
787            });
788            let mut y = 2;
789            BAR.set(&mut y, || {
790                assert!(BAR.is_set());
791                BAR.with(|slot| {
792                    assert_eq!(*slot, 2);
793                    *slot = 15;
794                });
795            });
796            assert_eq!(y, 15);
797            assert!(BAR.is_set());
798        });
799        assert!(!BAR.is_set());
800        assert_eq!(x, 42);
801    }
802
803    #[test]
804    fn mut_trait() {
805        scoped_thread_local!(static mut BAR: dyn std::io::Write);
806
807        assert!(!BAR.is_set());
808        let mut x = Vec::new();
809
810        BAR.set(&mut x, || {
811            assert!(BAR.is_set());
812            BAR.with(|slot| {
813                slot.write_all(&[1, 2, 3]).unwrap();
814            });
815        });
816        assert!(!BAR.is_set());
817        assert_eq!(x, [1, 2, 3]);
818    }
819
820    #[test]
821    fn hkt_mut_tuple() {
822        scoped_thread_local!(static mut BAR: for<'a> (&'a mut i32, &'a mut f32));
823
824        let mut x = 1;
825        let mut y = 2.0;
826
827        assert!(!BAR.is_set());
828        BAR.set((&mut x, &mut y), || {
829            assert!(BAR.is_set());
830            BAR.with(|(u, v)| {
831                assert_eq!(*u, 1);
832                assert_eq!(*v, 2.0);
833                assert!(!BAR.is_set());
834                *u = 3;
835                *v = 4.0;
836            });
837        });
838        assert!(!BAR.is_set());
839        assert_eq!(x, 3);
840        assert_eq!(y, 4.0);
841    }
842
843    #[test]
844    fn hkt_mut_trait() {
845        scoped_thread_local!(static mut BAR: for<'a> (&'a mut (dyn std::fmt::Display + 'static), &'a mut dyn std::any::Any));
846
847        assert!(!BAR.is_set());
848        let mut x = "Hello";
849        let mut y = 42;
850        BAR.set((&mut x, &mut y), || {
851            assert!(BAR.is_set());
852            BAR.with(|(u, _)| {
853                assert_eq!(u.to_string(), "Hello");
854            });
855        });
856        assert!(!BAR.is_set());
857    }
858
859    #[test]
860    fn hkt_mut_newtype() {
861        struct Foo<'a> {
862            x: &'a mut (dyn std::fmt::Display + 'a),
863            y: i32,
864        }
865
866        impl<'a, 'b> crate::ReborrowMut<'a> for Foo<'b> {
867            type Result = Foo<'a>;
868            fn reborrow_mut(&'a mut self) -> Self::Result {
869                Foo {
870                    x: self.x,
871                    y: self.y,
872                }
873            }
874        }
875
876        scoped_thread_local!(static mut BAR: for<'a> Foo<'a>);
877
878        assert!(!BAR.is_set());
879        let mut x = "Hello";
880        BAR.set(Foo { x: &mut x, y: 1 }, || {
881            assert!(BAR.is_set());
882            BAR.with(|foo| {
883                assert_eq!(foo.x.to_string(), "Hello");
884            });
885        });
886        assert!(!BAR.is_set());
887    }
888}