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 $(#[$attrs])*
220 $vis static $name: $name<'static> = {
221 type Hkt<$lt> = $ty;
222
223 {
224 use ::std::cell::Cell;
225 use ::std::option::Option;
226 use ::std::marker::Sync;
227 use ::std::ops::{FnOnce, Drop};
228 use ::std::thread::LocalKey;
229
230 thread_local!(static FOO: Cell<Option<Hkt<'static>>> = {
231 Cell::new(None)
232 });
233
234 unsafe impl Sync for $name<'static> {}
235
236 unsafe fn cast_to_static(x: Hkt<'_>) -> Hkt<'static> {
237 std::mem::transmute(x)
238 }
239
240 // This wrapper helps to ensure that the 'static lifetime is not visible
241 // to the safe code.
242 fn cast_from_static<'a, 'b>(x: &'a Hkt<'static>) -> Hkt<'b> where 'a: 'b {
243 *x
244 }
245
246 impl $name<'static> {
247 pub fn set<F, R>(&'static self, t: Hkt<'_>, f: F) -> R
248 where F: FnOnce() -> R
249 {
250 struct Reset {
251 key: &'static LocalKey<Cell<Option<Hkt<'static>>>>,
252 val: Option<Hkt<'static>>,
253 }
254 impl Drop for Reset {
255 fn drop(&mut self) {
256 self.key.with(|c| c.set(self.val.take()));
257 }
258 }
259 let prev = self.inner.with(|c| {
260 // Safety: we are only changing the lifetime. We enforce the
261 // lifetime constraints via the `Reset` struct.
262 c.replace(Some(unsafe { cast_to_static(t) }))
263 });
264 let _reset = Reset { key: self.inner, val: prev };
265 f()
266 }
267
268 pub fn with<F, R>(&'static self, f: F) -> R
269 where F: FnOnce(Hkt<'_>) -> R
270 {
271 let val = self.inner.with(|c| c.get());
272 let val = val.expect("cannot access a scoped thread local variable without calling `set` first");
273
274 // This also asserts that Hkt is covariant
275 f(cast_from_static(&val))
276 }
277
278 /// Test whether this TLS key has been `set` for the current thread.
279 pub fn is_set(&'static self) -> bool {
280 self.inner.with(|c| c.get().is_some())
281 }
282 }
283 $name {
284 inner: &FOO,
285 }
286 }
287 };
288 );
289 ($(#[$attrs:meta])* $vis:vis static mut $name:ident: $(#[$tattrs:meta])* for<$lt:lifetime> $ty:ty) => (
290 $(#[$tattrs])*
291 #[allow(non_camel_case_types)]
292 $vis struct $name<$lt> where ::std::cell::Cell<::std::option::Option<$ty>>: 'static {
293 inner: &$lt ::std::thread::LocalKey<::std::cell::Cell<::std::option::Option<$ty>>>,
294 }
295 $(#[$attrs])*
296 $vis static $name: $name<'static> = {
297 type Hkt<$lt> = $ty;
298
299 {
300 use ::std::cell::Cell;
301 use ::std::option::Option;
302 use ::std::marker::Sync;
303 use ::std::ops::{FnOnce, Drop};
304 use ::std::thread::LocalKey;
305
306 use $crate::ReborrowMut;
307
308 thread_local!(static FOO: Cell<Option<Hkt<'static>>> = {
309 Cell::new(None)
310 });
311
312 unsafe impl Sync for $name<'static> {}
313
314 unsafe fn cast_to_static(x: Hkt<'_>) -> Hkt<'static> {
315 std::mem::transmute(x)
316 }
317
318 // This wrapper helps to ensure that the 'static lifetime is not visible
319 // to the safe code.
320 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 {
321 //let y: &'b mut Hkt<'_> = unsafe { std::mem::transmute(x) };
322 <Hkt<'is_reborrow_mut_general_enough> as ReborrowMut<'_>>::reborrow_mut(x)
323 }
324
325 impl $name<'static> {
326 fn replace<F, R>(&'static self, value: Option<Hkt<'_>>, f: F) -> R
327 where F: FnOnce(Option<Hkt<'_>>) -> R
328 {
329 struct Reset {
330 key: &'static LocalKey<Cell<Option<Hkt<'static>>>>,
331 val: Option<Hkt<'static>>,
332 }
333 impl Drop for Reset {
334 fn drop(&mut self) {
335 self.key.with(|c| c.set(self.val.take()));
336 }
337 }
338 let prev = self.inner.with(move |c| {
339 // Safety: we are only changing the lifetime. We enforce the
340 // lifetime constraints via the `Reset` struct.
341 c.replace(value.map(|x| unsafe { cast_to_static(x) }))
342 });
343 let mut reset = Reset { key: self.inner, val: prev };
344 f(reset.val.as_mut().map(cast_from_static))
345 }
346
347 /// Inserts a value into this scoped thread local storage slot for a
348 /// duration of a closure.
349 pub fn set<F, R>(&'static self, t: Hkt<'_>, f: F) -> R
350 where F: FnOnce() -> R
351 {
352 self.replace(Some(t), |_| f())
353 }
354
355 /// Gets a value out of this scoped variable.
356 ///
357 /// This function takes a closure which receives the value of this
358 /// variable. For the duration of the closure, the key will appear
359 /// unset.
360 ///
361 /// # Panics
362 ///
363 /// This function will panic if `set` has not previously been called,
364 /// or if the call is nested inside another (multiple mutable borrows
365 /// of the same value are not allowed).
366 ///
367 pub fn with<F, R>(&'static self, f: F) -> R
368 where F: FnOnce(Hkt<'_>) -> R
369 {
370 self.replace(None, |val| f(val.expect("cannot access a scoped thread local variable without calling `set` first")))
371 }
372
373 /// Test whether this TLS key has been `set` for the current thread.
374 pub fn is_set(&'static self) -> bool {
375 self.replace(None, |prev| prev.is_some())
376 }
377 }
378 $name {
379 inner: &FOO,
380 }
381 }
382 };
383 );
384 ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
385 $(#[$attrs])*
386 $vis static $name: $crate::ScopedKey<$ty> = $crate::ScopedKey {
387 inner: {
388 thread_local!(static FOO: ::std::cell::Cell<::std::option::Option<&'static $ty>> = {
389 ::std::cell::Cell::new(None)
390 });
391 &FOO
392 },
393 };
394 );
395 ($(#[$attrs:meta])* $vis:vis static mut $name:ident: $ty:ty) => (
396 $(#[$attrs])*
397 $vis static $name: $crate::ScopedKeyMut<$ty> = $crate::ScopedKeyMut {
398 inner: {
399 thread_local!(static FOO: ::std::cell::Cell<::std::option::Option<&'static mut $ty>> = {
400 ::std::cell::Cell::new(None)
401 });
402 &FOO
403 },
404 };
405 );
406}
407
408/// Type representing a thread local storage key corresponding to a reference
409/// to the type parameter `T`.
410///
411/// Keys are statically allocated and can contain a reference to an instance of
412/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
413/// and `with`, both of which currently use closures to control the scope of
414/// their contents.
415pub struct ScopedKey<T: ?Sized + 'static> {
416 #[doc(hidden)]
417 pub inner: &'static LocalKey<Cell<Option<&'static T>>>,
418}
419
420unsafe impl<T: ?Sized + 'static> Sync for ScopedKey<T> {}
421
422unsafe fn cast_to_static<T: ?Sized + 'static>(x: &T) -> &'static T {
423 std::mem::transmute(src:x)
424}
425
426// This wrapper helps to ensure that the 'static lifetime is not visible
427// to the safe code.
428fn cast_from_static<'a, 'b, T: ?Sized + 'static>(x: &'a &T) -> &'b T
429where
430 'a: 'b,
431{
432 x
433}
434
435impl<T: ?Sized + 'static> ScopedKey<T> {
436 /// Inserts a value into this scoped thread local storage slot for a
437 /// duration of a closure.
438 ///
439 /// While `cb` is running, the value `t` will be returned by `get` unless
440 /// this function is called recursively inside of `cb`.
441 ///
442 /// Upon return, this function will restore the previous value, if any
443 /// was available.
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// use scoped_tls_hkt::scoped_thread_local;
449 ///
450 /// scoped_thread_local!(static FOO: u32);
451 ///
452 /// # fn main() {
453 /// FOO.set(&100, || {
454 /// let val = FOO.with(|v| *v);
455 /// assert_eq!(val, 100);
456 ///
457 /// // set can be called recursively
458 /// FOO.set(&101, || {
459 /// // ...
460 /// });
461 ///
462 /// // Recursive calls restore the previous value.
463 /// let val = FOO.with(|v| *v);
464 /// assert_eq!(val, 100);
465 /// });
466 /// # }
467 /// ```
468 pub fn set<F, R>(&'static self, t: &T, f: F) -> R
469 where
470 F: FnOnce() -> R,
471 {
472 struct Reset<T: ?Sized + 'static> {
473 key: &'static LocalKey<Cell<Option<&'static T>>>,
474 val: Option<&'static T>,
475 }
476 impl<T: ?Sized + 'static> Drop for Reset<T> {
477 fn drop(&mut self) {
478 self.key.with(|c| c.set(self.val));
479 }
480 }
481 let prev = self.inner.with(|c| {
482 // Safety: we are only changing the lifetime. We enforce the
483 // lifetime constraints via the `Reset` struct.
484 c.replace(Some(unsafe { cast_to_static(t) }))
485 });
486 let _reset = Reset {
487 key: self.inner,
488 val: prev,
489 };
490 f()
491 }
492
493 /// Gets a value out of this scoped variable.
494 ///
495 /// This function takes a closure which receives the value of this
496 /// variable.
497 ///
498 /// # Panics
499 ///
500 /// This function will panic if `set` has not previously been called.
501 ///
502 /// # Examples
503 ///
504 /// ```no_run
505 /// use scoped_tls_hkt::scoped_thread_local;
506 ///
507 /// scoped_thread_local!(static FOO: u32);
508 ///
509 /// # fn main() {
510 /// FOO.with(|slot| {
511 /// // work with `slot`
512 /// # drop(slot);
513 /// });
514 /// # }
515 /// ```
516 pub fn with<F, R>(&'static self, f: F) -> R
517 where
518 F: FnOnce(&T) -> R,
519 {
520 let val = self
521 .inner
522 .with(|c| c.get())
523 .expect("cannot access a scoped thread local variable without calling `set` first");
524 f(cast_from_static(&val))
525 }
526
527 /// Test whether this TLS key has been `set` for the current thread.
528 pub fn is_set(&'static self) -> bool {
529 self.inner.with(|c| c.get().is_some())
530 }
531}
532
533/// Type representing a thread local storage key corresponding to a mutable reference
534/// to the type parameter `T`.
535///
536/// Keys are statically allocated and can contain a reference to an instance of
537/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
538/// and `with`, both of which currently use closures to control the scope of
539/// their contents.
540///
541/// This differs from a `ScopedKey` because it provides access through a mutable
542/// reference. As a result, when the `with(..)` method is used to access the value,
543/// the key will appear unset whilst the closure is running. This is to prevent
544/// the value being borrowed a second time.
545pub struct ScopedKeyMut<T: ?Sized + 'static> {
546 #[doc(hidden)]
547 pub inner: &'static LocalKey<Cell<Option<&'static mut T>>>,
548}
549
550unsafe impl<T: ?Sized + 'static> Sync for ScopedKeyMut<T> {}
551
552unsafe fn cast_to_static_mut<T: ?Sized + 'static>(x: &mut T) -> &'static mut T {
553 std::mem::transmute(src:x)
554}
555
556// This wrapper helps to ensure that the 'static lifetime is not visible
557// to the safe code.
558fn cast_from_static_mut<'a, 'b, T: ?Sized + 'static>(x: &'a mut &mut T) -> &'b mut T
559where
560 'a: 'b,
561{
562 x
563}
564
565impl<T: ?Sized + 'static> ScopedKeyMut<T> {
566 fn replace<F, R>(&'static self, t: Option<&mut T>, f: F) -> R
567 where
568 F: FnOnce(Option<&mut T>) -> R,
569 {
570 struct Reset<T: ?Sized + 'static> {
571 key: &'static LocalKey<Cell<Option<&'static mut T>>>,
572 val: Option<&'static mut T>,
573 }
574 impl<T: ?Sized + 'static> Drop for Reset<T> {
575 fn drop(&mut self) {
576 self.key.with(|c| c.set(self.val.take()));
577 }
578 }
579 let prev = self.inner.with(move |c| {
580 // Safety: we are only changing the lifetime. We enforce the
581 // lifetime constraints via the `Reset` struct.
582 c.replace(t.map(|x| unsafe { cast_to_static_mut(x) }))
583 });
584 let mut reset = Reset {
585 key: self.inner,
586 val: prev,
587 };
588 f(reset.val.as_mut().map(cast_from_static_mut))
589 }
590
591 /// Inserts a value into this scoped thread local storage slot for a
592 /// duration of a closure.
593 pub fn set<F, R>(&'static self, t: &mut T, f: F) -> R
594 where
595 F: FnOnce() -> R,
596 {
597 self.replace(Some(t), |_| f())
598 }
599
600 /// Gets a value out of this scoped variable.
601 ///
602 /// This function takes a closure which receives the value of this
603 /// variable. For the duration of the closure, the key will appear
604 /// unset.
605 ///
606 /// # Panics
607 ///
608 /// This function will panic if `set` has not previously been called,
609 /// or if the call is nested inside another (multiple mutable borrows
610 /// of the same value are not allowed).
611 ///
612 pub fn with<F, R>(&'static self, f: F) -> R
613 where
614 F: FnOnce(&mut T) -> R,
615 {
616 self.replace(None, |val| {
617 f(val
618 .expect("cannot access a scoped thread local variable without calling `set` first"))
619 })
620 }
621
622 /// Test whether this TLS key has been `set` for the current thread.
623 pub fn is_set(&'static self) -> bool {
624 self.replace(None, |prev| prev.is_some())
625 }
626}
627
628#[cfg(test)]
629mod tests {
630 use std::cell::Cell;
631 use std::panic;
632 use std::sync::mpsc::{channel, Sender};
633 use std::thread;
634
635 scoped_thread_local!(static FOO: u32);
636
637 #[test]
638 fn smoke() {
639 scoped_thread_local!(static BAR: u32);
640
641 assert!(!BAR.is_set());
642 BAR.set(&1, || {
643 assert!(BAR.is_set());
644 BAR.with(|slot| {
645 assert_eq!(*slot, 1);
646 });
647 });
648 assert!(!BAR.is_set());
649 }
650
651 #[test]
652 fn cell_allowed() {
653 scoped_thread_local!(static BAR: Cell<u32>);
654
655 BAR.set(&Cell::new(1), || {
656 BAR.with(|slot| {
657 assert_eq!(slot.get(), 1);
658 });
659 });
660 }
661
662 #[test]
663 fn scope_item_allowed() {
664 assert!(!FOO.is_set());
665 FOO.set(&1, || {
666 assert!(FOO.is_set());
667 FOO.with(|slot| {
668 assert_eq!(*slot, 1);
669 });
670 });
671 assert!(!FOO.is_set());
672 }
673
674 #[test]
675 #[cfg_attr(miri, ignore)]
676 fn panic_resets() {
677 struct Check(Sender<u32>);
678 impl Drop for Check {
679 fn drop(&mut self) {
680 FOO.with(|r| {
681 self.0.send(*r).unwrap();
682 })
683 }
684 }
685
686 let (tx, rx) = channel();
687
688 // Temporarily suppress panic output, as it would interfere
689 // with the test harness output.
690 let prev_hook = panic::take_hook();
691 panic::set_hook(Box::new(|_| {
692 // Do nothing
693 }));
694
695 let t = thread::spawn(|| {
696 FOO.set(&1, || {
697 let _r = Check(tx);
698
699 FOO.set(&2, || panic!());
700 });
701 });
702
703 let res = t.join();
704 panic::set_hook(prev_hook);
705
706 assert_eq!(rx.recv().unwrap(), 1);
707 assert!(res.is_err());
708 }
709
710 #[test]
711 fn attrs_allowed() {
712 scoped_thread_local!(
713 /// Docs
714 static BAZ: u32
715 );
716
717 scoped_thread_local!(
718 #[allow(non_upper_case_globals)]
719 static quux: u32
720 );
721
722 let _ = BAZ;
723 let _ = quux;
724 }
725
726 #[test]
727 fn hkt_struct() {
728 #[derive(Copy, Clone)]
729 pub struct Foo<'a> {
730 x: &'a str,
731 y: &'a i32,
732 }
733 scoped_thread_local!(static BAR: for<'a> Foo<'a>);
734
735 assert!(!BAR.is_set());
736 BAR.set(Foo { x: "hi", y: &1 }, || {
737 assert!(BAR.is_set());
738 BAR.with(|slot| {
739 assert_eq!(slot.x, "hi");
740 assert_eq!(slot.y, &1);
741 });
742 });
743 assert!(!BAR.is_set());
744 }
745
746 #[test]
747 fn hkt_trait() {
748 scoped_thread_local!(static BAR: for<'a> &'a dyn std::fmt::Display);
749
750 assert!(!BAR.is_set());
751 BAR.set(&"Hello", || {
752 assert!(BAR.is_set());
753 BAR.with(|slot| {
754 assert_eq!(slot.to_string(), "Hello");
755 });
756 BAR.set(&42, || {
757 assert!(BAR.is_set());
758 BAR.with(|slot| {
759 assert_eq!(slot.to_string(), "42");
760 });
761 });
762 });
763 assert!(!BAR.is_set());
764 }
765
766 #[test]
767 fn mut_value() {
768 scoped_thread_local!(static mut BAR: i32);
769
770 assert!(!BAR.is_set());
771 let mut x = 0;
772
773 BAR.set(&mut x, || {
774 assert!(BAR.is_set());
775 BAR.with(|slot| {
776 assert!(!BAR.is_set());
777 assert_eq!(*slot, 0);
778 *slot = 42;
779 });
780 let mut y = 2;
781 BAR.set(&mut y, || {
782 assert!(BAR.is_set());
783 BAR.with(|slot| {
784 assert_eq!(*slot, 2);
785 *slot = 15;
786 });
787 });
788 assert_eq!(y, 15);
789 assert!(BAR.is_set());
790 });
791 assert!(!BAR.is_set());
792 assert_eq!(x, 42);
793 }
794
795 #[test]
796 fn mut_trait() {
797 scoped_thread_local!(static mut BAR: dyn std::io::Write);
798
799 assert!(!BAR.is_set());
800 let mut x = Vec::new();
801
802 BAR.set(&mut x, || {
803 assert!(BAR.is_set());
804 BAR.with(|slot| {
805 slot.write_all(&[1, 2, 3]).unwrap();
806 });
807 });
808 assert!(!BAR.is_set());
809 assert_eq!(x, [1, 2, 3]);
810 }
811
812 #[test]
813 fn hkt_mut_tuple() {
814 scoped_thread_local!(static mut BAR: for<'a> (&'a mut i32, &'a mut f32));
815
816 let mut x = 1;
817 let mut y = 2.0;
818
819 assert!(!BAR.is_set());
820 BAR.set((&mut x, &mut y), || {
821 assert!(BAR.is_set());
822 BAR.with(|(u, v)| {
823 assert_eq!(*u, 1);
824 assert_eq!(*v, 2.0);
825 assert!(!BAR.is_set());
826 *u = 3;
827 *v = 4.0;
828 });
829 });
830 assert!(!BAR.is_set());
831 assert_eq!(x, 3);
832 assert_eq!(y, 4.0);
833 }
834
835 #[test]
836 fn hkt_mut_trait() {
837 scoped_thread_local!(static mut BAR: for<'a> (&'a mut (dyn std::fmt::Display + 'static), &'a mut dyn std::any::Any));
838
839 assert!(!BAR.is_set());
840 let mut x = "Hello";
841 let mut y = 42;
842 BAR.set((&mut x, &mut y), || {
843 assert!(BAR.is_set());
844 BAR.with(|(u, _)| {
845 assert_eq!(u.to_string(), "Hello");
846 });
847 });
848 assert!(!BAR.is_set());
849 }
850
851 #[test]
852 fn hkt_mut_newtype() {
853 struct Foo<'a> {
854 x: &'a mut (dyn std::fmt::Display + 'a),
855 y: i32,
856 }
857
858 impl<'a, 'b> crate::ReborrowMut<'a> for Foo<'b> {
859 type Result = Foo<'a>;
860 fn reborrow_mut(&'a mut self) -> Self::Result {
861 Foo {
862 x: self.x,
863 y: self.y,
864 }
865 }
866 }
867
868 scoped_thread_local!(static mut BAR: for<'a> Foo<'a>);
869
870 assert!(!BAR.is_set());
871 let mut x = "Hello";
872 BAR.set(Foo { x: &mut x, y: 1 }, || {
873 assert!(BAR.is_set());
874 BAR.with(|foo| {
875 assert_eq!(foo.x.to_string(), "Hello");
876 });
877 });
878 assert!(!BAR.is_set());
879 }
880}
881