pui_core/scoped.rs
1//! A [`Scoped`] [`Identifier`]
2//!
3//! This works on two different principles.
4//!
5//! The first is that HRTB (higher rank type bounds) produces
6//! fresh lifetimes on each call, and they can't be unified
7//! against each other across multiple calls to `Scoped::with`.
8//!
9//! This allows [`Scoped::with`] to prevent the following at
10//! compile time:
11//! ```rust,compile_fail
12//! # use pui_core::Scoped;
13//! Scoped::with(|a: Scoped<'_>| {
14//! Scoped::with(|b: Scoped<'_>| {
15//! assert_eq!(a, b);
16//! })
17//! })
18//! ```
19//!
20//! This means that all `Scoped` types created with `Scoped::with`
21//! are guaranteed to be unique! If you can't compare them, then
22//! they can never compare equal.
23//!
24//! The second principle is based off of
25//! [`generativity`](https://crates.io/crates/generativity)
26//! where macros are used to generate the boilerplate to create
27//! unique lifetimes. You can read about the details in `generativity`'s
28//! documentation.
29//!
30//! This allows [`scope`] to produce unique `Scoped`s.
31//!
32//! ```rust,compile_fail
33//! # use pui_core::scope;
34//! scope!(a);
35//! scope!(b);
36//! assert_eq!(a, b);
37//! ```
38//!
39//! ## How to decide which to use
40//!
41//! `Scoped::with` generally produces better error messages, and tends
42//! to be easier to reason about. So it should be the first choice.
43//! However, if you are using some strange control flow or need early returns,
44//! and it would be easier to embed a `Scoped` directly in the current scope,
45//! then you should use the `scope` macro.
46//!
47
48use core::marker::PhantomData;
49
50use crate::{Identifier, Token};
51
52/// Create a new [`Scoped<'_>`](Scoped) with the given name
53/// without using a closure
54///
55/// ```rust
56/// pui_core::scope!(scope);
57/// ```
58///
59/// Two calls to `scope` will never return the same type
60/// (the difference is in the lifetimes)
61///
62/// ```rust, compile_fail
63/// pui_core::scope!(a);
64/// pui_core::scope!(b);
65/// assert_eq!(a, b);
66/// ```
67///
68/// ```rust
69/// pui_core::scope!(a);
70/// assert_eq!(a, a);
71/// ```
72#[macro_export]
73macro_rules! scope {
74 ($scope:ident) => {
75 let $scope = unsafe { $crate::scoped::Scoped::new_unchecked() };
76
77 let scope = ();
78 let assert_unique_scope = $crate::scoped::export::AssertUniqueScope::new(&$scope);
79 assert_unique_scope.bind(&scope);
80 };
81}
82
83#[doc(hidden)]
84pub mod export {
85 use core::marker::PhantomData;
86
87 pub struct AssertUniqueScope<'a>(PhantomData<super::Invariant<'a>>);
88
89 impl Drop for AssertUniqueScope<'_> {
90 #[inline(always)]
91 fn drop(&mut self) {}
92 }
93
94 impl<'a> AssertUniqueScope<'a> {
95 #[inline(always)]
96 pub fn new(_: &super::Scoped<'a>) -> Self { Self(PhantomData) }
97
98 #[inline(always)]
99 pub fn bind(&self, _: &'a ()) {}
100 }
101}
102
103struct Invariant<'a>(fn() -> *mut &'a ());
104
105/// A scoped [`Identifier`]
106#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
107pub struct Scoped<'a> {
108 invariant: PhantomData<Invariant<'a>>,
109}
110
111/// The [`Trivial`](crate::Trivial) [`Token`] generated by [`Scoped`]
112#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
113pub struct ScopedToken<'a> {
114 invariant: PhantomData<Invariant<'a>>,
115}
116
117impl<'scope> Scoped<'scope> {
118 #[doc(hidden)]
119 #[inline(always)]
120 pub unsafe fn new_unchecked() -> Self { Self { invariant: PhantomData } }
121
122 /// Call the given closure with a new `Scoped`
123 #[inline]
124 pub fn with<R, F: FnOnce(Scoped<'_>) -> R>(f: F) -> R { f(unsafe { Self::new_unchecked() }) }
125
126 /// Create a new scoped token
127 pub const fn token(&self) -> ScopedToken<'scope> { ScopedToken::new() }
128}
129
130impl ScopedToken<'_> {
131 /// Create a new scoped token
132 #[inline]
133 pub const fn new() -> Self { Self { invariant: PhantomData } }
134}
135
136impl crate::Init for ScopedToken<'_> {
137 const INIT: Self = Self { invariant: PhantomData };
138}
139
140impl crate::Trivial for ScopedToken<'_> {}
141unsafe impl Token for ScopedToken<'_> {}
142unsafe impl crate::OneShotIdentifier for Scoped<'_> {}
143unsafe impl<'a> Identifier for Scoped<'a> {
144 type Token = ScopedToken<'a>;
145
146 #[inline]
147 fn owns_token(&self, _: &Self::Token) -> bool { true }
148
149 #[inline]
150 fn token(&self) -> Self::Token { ScopedToken::new() }
151}
152
153use core::fmt;
154
155impl fmt::Debug for Scoped<'_> {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Scoped").finish() }
157}
158
159impl fmt::Debug for ScopedToken<'_> {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ScopedToken").finish() }
161}