Skip to main content

near_safe_cell/
lib.rs

1#![no_std]
2#![deny(
3    warnings,
4    missing_docs,
5    missing_debug_implementations,
6    rustdoc::broken_intra_doc_links,
7    clippy::all,
8    clippy::pedantic
9)]
10#![doc = include_str!("../Readme.md")]
11
12use core::{
13    cell::UnsafeCell,
14    default::Default,
15    fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex},
16    ops::{Deref, DerefMut, IndexMut},
17};
18
19/// A more ergonomic [`UnsafeCell`] replacement.
20///
21/// Note that unlike [`UnsafeCell<T>`], [`NearSafeCell<T>`] does implement [`Sync`]\(and [`RefUnwindSafe`](std::panic::RefUnwindSafe) if std is enabled) where `T: Sync`.
22/// This is because the only way to break its safety is by either calling [`NearSafeCell::get_mut_unsafe`](NearSafeCell::get_mut_unsafe)
23/// or dereferencing the pointer from [`NearSafeCell::get_(mut_)ptr`](NearSafeCell::get_ptr),
24/// both of which are themselves unsafe and have identical safety requirements that, if upheld properly, still guarantee [`Sync`] correctness.
25pub struct NearSafeCell<T>(UnsafeCell<T>);
26
27impl<T: Default> Default for NearSafeCell<T> {
28    fn default() -> Self {
29        Self::new(T::default())
30    }
31}
32
33impl<T> NearSafeCell<T> {
34    /// Constructs a new [`NearSafeCell`] wrapping a `T`.
35    pub const fn new(val: T) -> Self {
36        Self(UnsafeCell::new(val))
37    }
38    /// Consumes this [`NearSafeCell`], returning the wrapped `T`.
39    pub fn unwrap(self) -> T {
40        self.0.into_inner()
41    }
42    /// Returns a `&mut T` to the wrapped `T`, bypassing the borrow checker.
43    /// # Safety
44    /// There exists no other `&T` or `&mut T` to the wrapped `T` currently and until the returned `&mut T` is dropped.
45    pub unsafe fn get_mut_unsafe(&self) -> &mut T {
46        &mut *self.get_mut_ptr()
47    }
48    /// Returns a `&mut T::Output` to a part of the wrapped `T` indexed by `Idx`.
49    /// Helps avoiding having to even temporarily [`Self::get_mut_unsafe`] the whole T
50    /// just to get a subset of it, making it easier and more obvious to uphold the aliasing rules.
51    /// # Safety
52    /// There exists no other `&T` or `&mut T` the the whole wrapped `T`, nor to the `T::Output` part of it we are trying to index to.
53    pub unsafe fn index_mut_unchecked<Idx>(&self, idx: Idx) -> &mut T::Output
54    where
55        T: IndexMut<Idx>,
56    {
57        (*self.get_mut_ptr()).index_mut(idx)
58    }
59    /// Returns a `&T` to the wrapped `T`.
60    pub fn get(&self) -> &T {
61        // Safety: We have shared access to self and we only return it as a shared reference.
62        unsafe { &*self.get_ptr() }
63    }
64    /// Returns a `&mut T` to the wrapped `T`.
65    pub fn get_mut(&mut self) -> &mut T {
66        self.0.get_mut()
67    }
68    /// Returns a `*const T` to the wrapped `T`.
69    pub const fn get_ptr(&self) -> *const T {
70        self.0.get()
71    }
72    /// Returns a `*mut T` to the wrapped `T`.
73    pub const fn get_mut_ptr(&self) -> *mut T {
74        self.0.get()
75    }
76}
77
78// # Safety
79// The only way this impl could be unsafe would be if we
80// violated [`NearSafeCell::get_mut_unsafe`](NearSafeCell::get_mut_unsafe)s safety requirements,
81// at which point the fault lies with us and not this impl.
82unsafe impl<T: Sync> Sync for NearSafeCell<T> {}
83
84// # Safety
85// The only way this impl could be unsafe would be if we
86// violated [`NearSafeCell::get_mut_unsafe`](NearSafeCell::get_mut_unsafe)s safety requirements,
87// at which point the fault lies with us and not this impl.
88#[cfg(feature = "std")]
89use std::panic::RefUnwindSafe;
90#[cfg(feature = "std")]
91unsafe impl<T: RefUnwindSafe> RefUnwindSafe for NearSafeCell<T> {}
92
93impl<T> AsRef<T> for NearSafeCell<T> {
94    fn as_ref(&self) -> &T {
95        self.get()
96    }
97}
98impl<T> AsMut<T> for NearSafeCell<T> {
99    fn as_mut(&mut self) -> &mut T {
100        self.get_mut()
101    }
102}
103
104impl<T> Deref for NearSafeCell<T> {
105    type Target = T;
106    fn deref(&self) -> &Self::Target {
107        self.get()
108    }
109}
110impl<T> DerefMut for NearSafeCell<T> {
111    fn deref_mut(&mut self) -> &mut Self::Target {
112        self.get_mut()
113    }
114}
115
116impl<T: Debug> Debug for NearSafeCell<T> {
117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        f.debug_tuple("NearSafeCell").field(self.get()).finish()
119    }
120}
121
122impl<T: Display> Display for NearSafeCell<T> {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        self.get().fmt(f)
125    }
126}
127impl<T: Octal> Octal for NearSafeCell<T> {
128    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
129        self.get().fmt(f)
130    }
131}
132impl<T: LowerHex> LowerHex for NearSafeCell<T> {
133    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134        self.get().fmt(f)
135    }
136}
137impl<T: UpperHex> UpperHex for NearSafeCell<T> {
138    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139        self.get().fmt(f)
140    }
141}
142impl<T: Pointer> Pointer for NearSafeCell<T> {
143    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
144        self.get().fmt(f)
145    }
146}
147impl<T: Binary> Binary for NearSafeCell<T> {
148    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149        self.get().fmt(f)
150    }
151}
152impl<T: LowerExp> LowerExp for NearSafeCell<T> {
153    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154        self.get().fmt(f)
155    }
156}
157impl<T: UpperExp> UpperExp for NearSafeCell<T> {
158    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159        self.get().fmt(f)
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use core::ops::{Deref, DerefMut};
167
168    #[test]
169    fn usage() {
170        let mut cell = NearSafeCell::<usize>::default();
171        assert_eq!(cell.get(), &usize::default());
172        cell = NearSafeCell::new(24);
173
174        assert_eq!(cell.get_ptr(), cell.get() as *const _);
175        assert_eq!(cell.get_mut_ptr(), cell.get_mut() as *mut _);
176
177        assert_eq!(cell.as_ref(), &24);
178        assert_eq!(cell.as_mut(), &mut 24);
179        assert_eq!(cell.deref(), &24);
180        assert_eq!(cell.deref_mut(), &mut 24);
181
182        let interiorly_mutable = unsafe { cell.get_mut_unsafe() };
183        assert_eq!(interiorly_mutable, &mut 24);
184        *interiorly_mutable = 42;
185        drop(interiorly_mutable);
186
187        let shared = cell.get();
188        let shared2 = cell.get();
189        assert_eq!(shared, &42);
190        assert_eq!(shared, shared2);
191
192        let mutable = cell.get_mut();
193        assert_eq!(mutable, &mut 42);
194        *mutable = 242;
195
196        assert_eq!(cell.unwrap(), 242);
197    }
198
199    #[test]
200    fn indexing() {
201        use core::ops::Index;
202        let cell = NearSafeCell::new([1, 2, 3, 4, 5]);
203        {
204            let sub_0 = cell.index(2..);
205            let sub_1 = unsafe { cell.index_mut_unchecked(..2) };
206            assert_eq!(sub_0, [3, 4, 5]);
207            assert_eq!(sub_1, [1, 2]);
208            sub_1.copy_from_slice(&[24, 42]);
209            assert_eq!(sub_0, [3, 4, 5]);
210            assert_eq!(sub_1, [24, 42]);
211        }
212        assert_eq!(cell.unwrap(), [24, 42, 3, 4, 5]);
213    }
214
215    include!("test_utilities.rs");
216    #[test]
217    fn formatting() {
218        let mut buffer = [0u8; 32];
219        let cell = NearSafeCell::new(42);
220        assert_eq!(
221            format(&mut buffer, format_args!("{:?}", cell)).unwrap(),
222            "NearSafeCell(42)"
223        );
224        assert_eq!(format(&mut buffer, format_args!("{}", cell)).unwrap(), "42");
225        assert_eq!(
226            format(&mut buffer, format_args!("{:o}", cell)).unwrap(),
227            "52"
228        );
229        assert_eq!(
230            format(&mut buffer, format_args!("{:x}", cell)).unwrap(),
231            "2a"
232        );
233        assert_eq!(
234            format(&mut buffer, format_args!("{:X}", cell)).unwrap(),
235            "2A"
236        );
237        assert_eq!(
238            format(&mut buffer, format_args!("{:b}", cell)).unwrap(),
239            "101010"
240        );
241        assert_eq!(
242            format(&mut buffer, format_args!("{:e}", cell)).unwrap(),
243            "4.2e1"
244        );
245        assert_eq!(
246            format(&mut buffer, format_args!("{:E}", cell)).unwrap(),
247            "4.2E1"
248        );
249        let cell = NearSafeCell::new(42 as *const u8);
250        assert_eq!(
251            format(&mut buffer, format_args!("{:p}", cell)).unwrap(),
252            "0x2a"
253        );
254    }
255}