scoped_ref/lib.rs
1//! [](https://crates.io/crates/scoped-ref)
2//! [](https://docs.rs/scoped-ref)
3//! [](https://github.com/andersjel/scoped-ref/actions/workflows/ci.yml)
4//! [](https://github.com/andersjel/scoped-ref/blob/main/LICENSE-MIT)
5//!
6//! # Introduction
7//!
8//! This crate provides safe references with runtime-checked lifetimes. While a
9//! reference is in use, attempts to drop the referenced value will block.
10//!
11//! The main entry-point for this library is the [`Scope`] struct.
12//!
13//! Additional examples are available in the [`examples`] module.
14//!
15//! # Features
16//!
17//! Feature | Default | Description
18//! --- | --- | ---
19//! docs | no | Used internally when building documentation.
20//!
21//! # Similar Crates
22//! - The [lien] crate provides a similar construction without weak references
23//! but with a more flexible API and `no_std` support.
24//! - The [scoped_reference] crate provides runtime checked references that
25//! aborts the program (without first running panic handlers) on violations.
26//! This is preferable to deadlocking, especially in single-threaded use
27//! cases.
28//!
29//! [lien]: https://crates.io/crates/lien
30//! [scoped_reference]: https://crates.io/crates/scoped_reference
31//! [smol]: https://crates.io/crates/smol
32//! [`smol::Executor<'a>`]: https://docs.rs/smol/2.0.2/smol/struct.Executor.html
33
34#![warn(clippy::pedantic, clippy::allow_attributes)]
35
36pub mod examples;
37
38#[cfg(test)]
39mod tests;
40
41use std::{
42 fmt::{Debug, Display},
43 ops::Deref,
44 ptr::NonNull,
45 sync::{PoisonError, RwLock, RwLockReadGuard},
46};
47
48fn ignore_poison<T>(v: Result<T, PoisonError<T>>) -> T {
49 v.unwrap_or_else(PoisonError::into_inner)
50}
51
52/// Returned when [`Scope::assign()`] is called un an already assigned
53/// [`Scope`].
54pub struct AssignedError<F> {
55 /// The `body` passed to [`Scope::assign()`].
56 pub body: F,
57}
58
59impl<F> Debug for AssignedError<F> {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("AssignError").finish_non_exhaustive()
62 }
63}
64
65/// The display implementation formats as `"Scope already assigned"`.
66impl<F> Display for AssignedError<F> {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 f.write_str("Scope already assigned")
69 }
70}
71
72/// Manages the lifetime of scoped references created with this library.
73///
74/// A [`Scope<T>`] acts effectively like an `Option<&'a T>` (but without the
75/// lifetime parameter). When the [`Scope<T>`] contains a reference, it
76/// is said to be *assigned*, when it does not it is *unassigned*.
77///
78/// An assigned [`Scope<T>`] can be used to produce [`StrongRef<T>`] instances
79/// that dereference to the contained `&T`. A [`Scope<T>`] cannot become
80/// unassigned while [strong references] exist.
81///
82/// Both unassigned and assigned [`Scope<T>`] instances can be used to produce
83/// [`WeakRef<T>`] instances, that can later be [upgraded] to [`StrongRef<T>`]
84/// instances.
85///
86/// To assign a `&T`, to a [`Scope<T>`] you use the [`Scope::assign()`] method.
87/// The method takes `&T` to assign and *body* to execute. When the *body*
88/// returns, and all [`StrongRef<T>`] instances created from the [`Scope<T>`]
89/// are dropped, [`Scope::assign()`] returns and the [`Scope<T>`] is again
90/// unassigned. This ensures that no dangling use of the `&T` can take place.
91///
92/// # Example
93///
94/// ```
95/// # use scoped_ref::{StrongRef, Scope};
96/// fn read_value(reference: StrongRef<i32>) -> i32 {
97/// *reference
98/// }
99///
100/// let scope = Scope::new();
101/// let value = 42;
102///
103/// let result = scope
104/// .assign(&value, || read_value(scope.strong_ref().unwrap()))
105/// .unwrap();
106///
107/// assert_eq!(42, result);
108/// ```
109///
110/// [strong references]: StrongRef
111/// [upgraded]: WeakRef::upgrade()
112#[derive(Default, Debug)]
113pub struct Scope<T: ?Sized> {
114 value: RwLock<Option<NonNull<T>>>,
115}
116
117// Safety:
118// A Scope<T> effectively owns a &T. It is Sync and Send if T is Sync.
119unsafe impl<T: Sync + ?Sized> Send for Scope<T> {}
120unsafe impl<T: Sync + ?Sized> Sync for Scope<T> {}
121
122/// A weak reference obtained from a [`Scope`].
123///
124/// A weak reference cannot be dereferenced directly, but must first be upgraded
125/// to a [`StrongRef`] with [`WeakRef::upgrade()`].
126///
127/// Note, that the lifetime parameter `'scope` of a [`WeakRef<'scope, T>`] is
128/// that of the associated [`Scope<T>`], _not_ whatever `&T` that the
129/// [`Scope<T>`] happens to be assigned.
130#[derive(Debug, Clone)]
131pub struct WeakRef<'scope, T: ?Sized> {
132 source: &'scope Scope<T>,
133}
134
135/// A strong reference obtained from a [`Scope`].
136///
137/// A [`StrongRef<'_, T>`] can be [dereferenced][Deref] to a `&T`.
138///
139/// A [`StrongRef`] to a [`Scope`] can only be created while [`Scope::assign()`]
140/// is being executed, and [`Scope::assign()`] will not return until all
141/// existing [`StrongRef`] instances have been dropped.
142///
143/// Note, that the lifetime parameter `'scope` of a [`StrongRef<'scope, T>`] is
144/// that of the associated [`Scope<T>`], _not_ the `&T` that the [`Scope<T>`] is
145/// assigned.
146#[derive(Debug)]
147pub struct StrongRef<'scope, T: ?Sized> {
148 source: &'scope Scope<T>,
149 guard: RwLockReadGuard<'scope, Option<NonNull<T>>>,
150}
151
152impl<'scope, T: ?Sized> StrongRef<'scope, T> {
153 /// Downgrade to a [`WeakRef`].
154 #[must_use]
155 pub fn downgrade(&self) -> WeakRef<'scope, T> {
156 self.source.weak_ref()
157 }
158}
159
160impl<T: ?Sized> Clone for StrongRef<'_, T> {
161 fn clone(&self) -> Self {
162 self.source.strong_ref().unwrap()
163 }
164}
165
166unsafe impl<T: Sync + ?Sized> Sync for StrongRef<'_, T> {}
167
168impl<T: ?Sized> Scope<T> {
169 /// Construct a new [`Scope`].
170 ///
171 /// This creates an _unassigned_ [`Scope`]; assign a target using
172 /// [`Self::assign()`].
173 ///
174 /// # Note
175 /// [`Scope`] and [`WeakRef`] instances can also be created statically:
176 ///
177 /// ```
178 /// # use scoped_ref::*;
179 /// static SCOPE: Scope<i32> = Scope::new();
180 /// static WEAK_REF: WeakRef<'static, i32> = SCOPE.weak_ref();
181 /// let value = SCOPE.assign(&42, || *WEAK_REF.upgrade().unwrap()).unwrap();
182 /// assert_eq!(value, 42)
183 /// ```
184 #[must_use]
185 pub const fn new() -> Self {
186 Self {
187 value: RwLock::new(None),
188 }
189 }
190
191 /// Run `body` with `target` assigned to `self`.
192 ///
193 /// If `self` is not already assigned a target when this method is called,
194 /// then:
195 ///
196 /// 1. While `body` is executing, [strong references] to `self` will
197 /// dereference to `target`.
198 /// 2. When `body` returns, this method will block until there are no
199 /// [strong references] left to `self` before returning.
200 /// 3. The value returned by `body` is returned by this method. When this
201 /// method returns, `self` is again considered unassigned.
202 ///
203 /// This method has no effect if `self` is already assigned a target when
204 /// called. In this case, the given `body` is _not_ executed and is instead
205 /// returned back to the caller in an [`AssignedError`].
206 ///
207 /// # Deadlocks
208 /// If any [strong references] are leaked, this method will never return.
209 ///
210 /// # Errors
211 /// Will return [`AssignedError`] if `self` is already assigned a target
212 /// when called.
213 ///
214 /// # Notes
215 /// Pre-existing [weak references] to `self` will also be affected by this
216 /// method.
217 ///
218 /// ```
219 /// let scope = scoped_ref::Scope::new();
220 /// let weak_ref = scope.weak_ref();
221 /// let sample = || *weak_ref.upgrade().unwrap();
222 ///
223 /// let value = scope.assign(&42, sample).unwrap();
224 /// assert_eq!(value, 42);
225 ///
226 /// let value = scope.assign(&43, sample).unwrap();
227 /// assert_eq!(value, 43);
228 /// ```
229 ///
230 /// [strong references]: StrongRef
231 /// [weak references]: WeakRef
232 pub fn assign<R, F: FnOnce() -> R>(&self, target: &T, body: F) -> Result<R, AssignedError<F>> {
233 {
234 let mut guard = ignore_poison(self.value.write());
235 if guard.is_some() {
236 return Err(AssignedError { body });
237 }
238 *guard = Some(NonNull::from_ref(target));
239 }
240 let _clear = EmptyScopeOnDrop { target: self };
241 Ok(body())
242 }
243
244 /// Construct a [`WeakRef`] to this [`Scope`].
245 ///
246 /// The returned [`WeakRef`] can be upgraded to a [`StrongRef`] with
247 /// [`WeakRef::upgrade`] but only while a value is
248 /// [assigned][Scope::assign] to this [`Scope`].
249 #[must_use]
250 pub const fn weak_ref(&self) -> WeakRef<'_, T> {
251 WeakRef { source: self }
252 }
253
254 /// Construct a [`StrongRef`] to this [`Scope`].
255 ///
256 /// This will return [`None`] if no value is currently
257 /// [assigned][Scope::assign] to this [`Scope`].
258 #[must_use]
259 pub fn strong_ref(&self) -> Option<StrongRef<'_, T>> {
260 self.weak_ref().upgrade()
261 }
262}
263
264impl<'scope, T: ?Sized> WeakRef<'scope, T> {
265 /// Upgrade this [`WeakRef`] to a [`StrongRef`].
266 ///
267 /// This method returns [`None`] unless a value is currently
268 /// [assigned][Scope::assign] to the associated [`Scope`].
269 #[must_use]
270 pub fn upgrade(&self) -> Option<StrongRef<'scope, T>> {
271 let guard = ignore_poison(self.source.value.read());
272
273 // Never construct a StrongRef containing None.
274 if guard.is_none() {
275 None
276 } else {
277 Some(StrongRef {
278 source: self.source,
279 guard,
280 })
281 }
282 }
283}
284
285impl<T: ?Sized> Deref for StrongRef<'_, T> {
286 type Target = T;
287
288 fn deref(&self) -> &Self::Target {
289 // Safety:
290 // - unwrap_unchecked() is safe because we never construct a StrongRef
291 // containing None (see WeakRef::upgrade()).
292 // - as_ref() is safe because we do not return from Scope::assign() before
293 // taking a write lock, and this cannot happen while self.guard exists. This
294 // guarantees that the reference is valid until `self` is dropped.
295 unsafe { self.guard.unwrap_unchecked().as_ref() }
296 }
297}
298
299struct EmptyScopeOnDrop<'a, T: ?Sized> {
300 target: &'a Scope<T>,
301}
302
303impl<T: ?Sized> Drop for EmptyScopeOnDrop<'_, T> {
304 fn drop(&mut self) {
305 *ignore_poison(self.target.value.write()) = None;
306 }
307}