mapped_guard/
lib.rs

1//! # A note on the implementation
2//!
3//! This library is a workaround until mapped guards become available in the standard library and uses boxing internally.
4//!
5//! While it's in practice likely possible to implement this more efficiently (without boxing) for many guards,
6//! this isn't safe except for mutable borrows where the documentation explicitly states they are write-through.
7
8#![doc(html_root_url = "https://docs.rs/mapped-guard/0.0.1")]
9#![warn(clippy::pedantic)]
10
11use core::{mem::transmute, ops::Deref};
12use std::{
13	cell::Ref,
14	sync::{MutexGuard, RwLockReadGuard, RwLockWriteGuard},
15};
16
17#[cfg(doctest)]
18pub mod readme {
19	doc_comment::doctest!("../README.md");
20}
21
22// There are a few requirements that must hold for this library to be sound:
23// I: G is movable independently from R (base premise)
24// II: It is impossible to separate G and R, except if that transformation is guarateed to keep R valid which is enabled by III.
25// III: Consumers can't acquire a direct reference to `target` while consuming `self`.
26#[derive(Debug)]
27pub struct MappedGuard<G, R> {
28	guard: G,
29	target: R,
30}
31
32impl<G, R> MappedGuard<G, R> {
33	pub fn new(guard: G, target: R) -> Self {
34		Self { guard, target }
35	}
36}
37
38impl<G, R: Deref> Deref for MappedGuard<G, R> {
39	// Safety: III only holds if the deref defers to R's Target because MappedGuard is UnpinDereferenced!
40	type Target = R::Target;
41
42	#[inline]
43	fn deref(&self) -> &Self::Target {
44		&self.target
45	}
46}
47
48/// Marker types for guards that are mapped by first being boxed (because they make no guarantees that their target reference can be detached from their location in memory.)
49/// Technically this could be anything, but the operation only really makes sense for guards, in order to return the mapping from a function.
50pub trait BoxedMapped {}
51impl<'a, T> BoxedMapped for MutexGuard<'a, T> {}
52impl<'a, T> BoxedMapped for RwLockReadGuard<'a, T> {}
53impl<'a, T> BoxedMapped for RwLockWriteGuard<'a, T> {}
54impl<'a, T> BoxedMapped for Ref<'a, T> {}
55
56// TODO: This is a bit inefficient. Provide another implementation that uses a flatter mapping if R is Deref.
57impl<G, R> BoxedMapped for MappedGuard<G, R> {}
58
59//TODO: Mention this in the struct documentation!
60impl<G, R1, R> From<MappedGuard<MappedGuard<G, R1>, R>> for MappedGuard<G, R> {
61	/// Flattens nested `MappedGuard` instances.
62	#[inline]
63	fn from(from: MappedGuard<MappedGuard<G, R1>, R>) -> Self {
64		Self {
65			guard: from.guard.guard,
66			target: from.target,
67		}
68	}
69}
70
71pub trait MapGuard<'a, G, R1, R2: 'a> {
72	fn map_guard(self, map: impl FnOnce(R1) -> R2) -> MappedGuard<G, R2>;
73
74	//TODO: What's the naming convention for this?
75	fn maybe_map_guard(
76		self,
77		maybe_map: impl FnOnce(R1) -> Option<R2>,
78	) -> Option<MappedGuard<G, R2>>;
79}
80pub trait TryMapGuard<'a, G, R1, R2: 'a, E: 'a> {
81	#[allow(clippy::missing_errors_doc)]
82	fn try_map_guard(
83		self,
84		try_map: impl FnOnce(R1) -> Result<R2, E>,
85	) -> Result<MappedGuard<G, R2>, E>;
86}
87
88//TODO: These should be default implementations and associated types instead.
89impl<'a, G: BoxedMapped + 'a, R: 'a> MapGuard<'a, Box<G>, &'a G, R> for G {
90	#[inline]
91	fn map_guard(self, map: impl FnOnce(&'a G) -> R) -> MappedGuard<Box<G>, R> {
92		let boxed = Box::new(self);
93		MappedGuard {
94			target: map(unsafe {
95				//SAFETY: `guard` can't be dropped independently from `target`.
96				detach_borrow(&*boxed)
97			}),
98			guard: boxed,
99		}
100	}
101
102	#[inline]
103	fn maybe_map_guard(
104		self,
105		maybe_map: impl FnOnce(&'a G) -> Option<R>,
106	) -> Option<MappedGuard<Box<G>, R>> {
107		let boxed = Box::new(self);
108		maybe_map(unsafe {
109			//SAFETY: `guard` can't be dropped independently from `target`.
110			detach_borrow(&*boxed)
111		})
112		.map(|target| MappedGuard {
113			target,
114			guard: boxed,
115		})
116	}
117}
118impl<'a, G: BoxedMapped + 'a, R: 'a, E: 'a> TryMapGuard<'a, Box<G>, &'a G, R, E> for G {
119	#[inline]
120	fn try_map_guard(
121		self,
122		try_map: impl FnOnce(&'a G) -> Result<R, E>,
123	) -> Result<MappedGuard<Box<G>, R>, E> {
124		let boxed = Box::new(self);
125		Ok(MappedGuard {
126			target: try_map(unsafe {
127				//SAFETY: `guard` can't be dropped independently from `target`.
128				detach_borrow(&*boxed)
129			})?,
130			guard: boxed,
131		})
132	}
133}
134
135#[inline]
136unsafe fn detach_borrow<'a, 'b, T>(reference: &'a T) -> &'b T {
137	transmute(reference)
138}