1//! `Cell` variant for (scoped) existential lifetimes.
2
3use std::cell::Cell;
4use std::mem;
5use std::ops::{Deref, DerefMut};
6
7/// Type lambda application, with a lifetime.
8#[allow(unused_lifetimes)]
9pub trait ApplyL<'a> {
10 type Out;
11}
12
13/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
14pub trait LambdaL: for<'a> ApplyL<'a> {}
15
16impl<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`
20pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out);
21
22impl<'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
29impl<'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
35pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
36
37impl<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