1 | //! `Cell` variant for (scoped) existential lifetimes. |
2 | |
3 | use std::cell::Cell; |
4 | use std::mem; |
5 | use std::ops::{Deref, DerefMut}; |
6 | |
7 | /// Type lambda application, with a lifetime. |
8 | #[allow (unused_lifetimes)] |
9 | pub trait ApplyL<'a> { |
10 | type Out; |
11 | } |
12 | |
13 | /// Type lambda taking a lifetime, i.e., `Lifetime -> Type`. |
14 | pub trait LambdaL: for<'a> ApplyL<'a> {} |
15 | |
16 | impl<T: for<'a> ApplyL<'a>> LambdaL for T {} |
17 | |
18 | // HACK(eddyb) work around projection limitations with a newtype |
19 | // FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out` |
20 | pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out); |
21 | |
22 | impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> { |
23 | type Target = <T as ApplyL<'b>>::Out; |
24 | fn deref(&self) -> &Self::Target { |
25 | self.0 |
26 | } |
27 | } |
28 | |
29 | impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { |
30 | fn deref_mut(&mut self) -> &mut Self::Target { |
31 | self.0 |
32 | } |
33 | } |
34 | |
35 | pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>); |
36 | |
37 | impl<T: LambdaL> ScopedCell<T> { |
38 | pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self { |
39 | ScopedCell(Cell::new(value)) |
40 | } |
41 | |
42 | /// Sets the value in `self` to `replacement` while |
43 | /// running `f`, which gets the old value, mutably. |
44 | /// The old value will be restored after `f` exits, even |
45 | /// by panic, including modifications made to it by `f`. |
46 | pub fn replace<'a, R>( |
47 | &self, |
48 | replacement: <T as ApplyL<'a>>::Out, |
49 | f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R, |
50 | ) -> R { |
51 | /// Wrapper that ensures that the cell always gets filled |
52 | /// (with the original state, optionally changed by `f`), |
53 | /// even if `f` had panicked. |
54 | struct PutBackOnDrop<'a, T: LambdaL> { |
55 | cell: &'a ScopedCell<T>, |
56 | value: Option<<T as ApplyL<'static>>::Out>, |
57 | } |
58 | |
59 | impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> { |
60 | fn drop(&mut self) { |
61 | self.cell.0.set(self.value.take().unwrap()); |
62 | } |
63 | } |
64 | |
65 | let mut put_back_on_drop = PutBackOnDrop { |
66 | cell: self, |
67 | value: Some(self.0.replace(unsafe { |
68 | let erased = mem::transmute_copy(&replacement); |
69 | mem::forget(replacement); |
70 | erased |
71 | })), |
72 | }; |
73 | |
74 | f(RefMutL(put_back_on_drop.value.as_mut().unwrap())) |
75 | } |
76 | |
77 | /// Sets the value in `self` to `value` while running `f`. |
78 | pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R { |
79 | self.replace(value, |_| f()) |
80 | } |
81 | } |
82 | |