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(src: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(src: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}
889