option_cell/
lib.rs

1//! ## OptionCell: OnceCell but derivable from Option
2//!
3//! This library provides an equivalent of [OnceCell](https://doc.rust-lang.org/stable/std/cell/struct.OnceCell.html), but it guarantees layout compatibility with `Option<T>`, providing additional transmute helpers.
4//!
5//! ## Known use-cases
6//!
7//! - Implementing the [unification algorithm](https://en.wikipedia.org/wiki/Unification_(computer_science)) without exposing the interior mutability to the user or unnecessarily cloning the value.
8//!
9//! ## Usage
10//!
11//! ```txt
12//! cargo add option-cell
13//! ```
14//!
15//! ```rust
16//! use option_cell::OptionCell;
17//!
18//! let mut options = vec![None, None];
19//! let cells = OptionCell::from_mut_slice(&mut options);
20//! cells[0].set(1).unwrap();
21//! ```
22
23use std::cell::UnsafeCell;
24use std::fmt;
25
26/// An equivalent of [std::cell::OnceCell](https://doc.rust-lang.org/stable/std/cell/struct.OnceCell.html) or [once_cell::unsync::OnceCell](https://docs.rs/once_cell/latest/once_cell/unsync/struct.OnceCell.html)
27/// with an additional transmute helper.
28/// To guarantee the helper's safety, it is defined as a different type from the original OnceCell.
29// Unlike the original OnceCell, we need #[repr(transparent)] to guarantee the layout compatibility
30#[repr(transparent)]
31pub struct OptionCell<T> {
32    // Ownership invariant: same as Option<T>.
33    //
34    // Shared invariant:
35    // It has internally two modes: read and write.
36    // - It is in write mode if the value is None
37    //   or the control is in a critical section
38    //   and the value was None when the critical section started.
39    // - It is in read mode if the value is Some(_)
40    //   and the write mode is not extended in a critical section in a manner described above.
41    //
42    // Invariant changes between read and write modes:
43    // - In read mode, one has read access to the whole Option<T> (whether or not in a critical section).
44    // - In write mode, one has write access to the whole Option<T> when in a critical section.
45    inner: UnsafeCell<Option<T>>,
46}
47
48impl<T> OptionCell<T> {
49    /// Safety requirement: critical sections must not be nested.
50    unsafe fn critical_read_section<R, F>(&self, f: F) -> R
51    where
52        F: FnOnce(&Option<T>) -> R,
53    {
54        f(&*self.inner.get())
55    }
56
57    /// Safety requirement: critical sections must not be nested.
58    /// Additionally, the caller must ensure that the value is None before entering the section.
59    unsafe fn critical_write_section<R, F>(&self, f: F) -> R
60    where
61        F: FnOnce(&mut Option<T>) -> R,
62    {
63        f(&mut *self.inner.get())
64    }
65
66    /// Creates a new empty cell.
67    pub const fn new() -> Self {
68        Self {
69            inner: UnsafeCell::new(None),
70        }
71    }
72
73    /// Gets the reference to the underlying value.
74    /// Returns `None` if the cell is empty.
75    pub fn get(&self) -> Option<&T> {
76        // Safety: critical section can always read. Then,
77        // - If it is Some(_), it is in read mode.
78        //   It is safe to return references as the caller also has the read access.
79        // - If it is None, it returns the None value.
80        //   That means no references are exposed to the caller.
81        //
82        // It does not use the critical section helper to extend the reference's lifetime.
83        // Nevertheless it constitutes a critical section.
84        unsafe { &*self.inner.get() }.as_ref()
85    }
86
87    /// Gets the mutable reference to the underlying Option.
88    ///
89    /// Unlike the original OnceCell, this method returns a mutable reference to the whole Option<T>,
90    /// as the layout is guaranteed.
91    pub fn get_mut(&mut self) -> &mut Option<T> {
92        // Safety: the ownership invariant is the same as Option<T>
93        unsafe { &mut *self.inner.get() }
94    }
95
96    /// Sets the contents of this cell to `value`.
97    pub fn set(&self, value: T) -> Result<(), T> {
98        let is_none = unsafe { self.critical_read_section(|opt| opt.is_none()) };
99        if is_none {
100            unsafe {
101                self.critical_write_section(|opt| *opt = Some(value));
102            }
103            Ok(())
104        } else {
105            Err(value)
106        }
107    }
108
109    /// Gets the contents of the cell, initializing with `f` if the cell was empty.
110    pub fn get_or_init<F>(&self, f: F) -> &T
111    where
112        F: FnOnce() -> T,
113    {
114        if let Some(value) = self.get() {
115            value
116        } else {
117            let value = f();
118            if self.set(value).is_err() {
119                panic!("Recursive initialization within get_or_init");
120            }
121            self.get().unwrap()
122        }
123    }
124
125    /// Consumes the cell, returning the wrapped Option<T>.
126    pub fn into_inner(self) -> Option<T> {
127        self.inner.into_inner()
128    }
129
130    /// Takes the value out of this cell, leaving it empty.
131    pub fn take(&mut self) -> Option<T> {
132        self.get_mut().take()
133    }
134
135    /// Converts an existing mutable reference into OptionCell.
136    pub fn from_mut(slice: &mut Option<T>) -> &mut Self {
137        // Safety: layout is compatible as observed in Cell.
138        // The ownership invariant is the same.
139        unsafe { &mut *(slice as *mut Option<T> as *mut Self) }
140    }
141
142    /// Converts an existing mutable slice into a slice of OptionCell.
143    pub fn from_mut_slice(slice: &mut [Option<T>]) -> &mut [Self] {
144        // Safety: layout is compatible as observed in Cell.
145        // The ownership invariant is the same.
146        unsafe { std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut Self, slice.len()) }
147    }
148}
149
150impl<T> From<Option<T>> for OptionCell<T> {
151    fn from(opt: Option<T>) -> Self {
152        Self {
153            inner: UnsafeCell::new(opt),
154        }
155    }
156}
157
158impl<T> Default for OptionCell<T> {
159    fn default() -> Self {
160        OptionCell::from(None)
161    }
162}
163
164impl<T> From<OptionCell<T>> for Option<T> {
165    fn from(cell: OptionCell<T>) -> Self {
166        cell.into_inner()
167    }
168}
169
170impl<T> Clone for OptionCell<T>
171where
172    T: Clone,
173{
174    fn clone(&self) -> Self {
175        OptionCell::from(self.get().cloned())
176    }
177}
178
179impl<T> PartialEq<OptionCell<T>> for OptionCell<T>
180where
181    T: PartialEq<T>,
182{
183    fn eq(&self, other: &Self) -> bool {
184        self.get() == other.get()
185    }
186
187    fn ne(&self, other: &Self) -> bool {
188        self.get() != other.get()
189    }
190}
191
192impl<T> fmt::Debug for OptionCell<T>
193where
194    T: fmt::Debug,
195{
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.debug_tuple("OptionCell").field(&self.get()).finish()
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_new_get() {
207        let cell = OptionCell::<i32>::new();
208        assert_eq!(cell.get(), None);
209    }
210
211    #[test]
212    fn test_set_get() {
213        let cell = OptionCell::<i32>::new();
214        let cell_ref1 = &cell;
215        let cell_ref2 = &cell;
216        let cell_ref3 = &cell;
217        assert_eq!(cell_ref1.get(), None);
218        cell_ref2.set(42).unwrap();
219        assert_eq!(cell_ref3.get(), Some(&42));
220    }
221
222    #[test]
223    fn test_set_fail_get() {
224        let cell = OptionCell::<i32>::new();
225        cell.set(42).unwrap();
226        let cell_ref1 = &cell;
227        let cell_ref2 = &cell;
228        assert!(cell_ref1.set(43).is_err());
229        assert_eq!(cell_ref2.get(), Some(&42));
230    }
231
232    #[test]
233    fn test_from_mut() {
234        {
235            let mut opt = Some(42);
236            let cell = OptionCell::from_mut(&mut opt);
237            let cell_ref1 = &*cell;
238            let cell_ref2 = &*cell;
239            assert_eq!(cell_ref1.get(), Some(&42));
240            assert!(cell_ref2.set(43).is_err());
241        }
242        {
243            let mut opt = None;
244            let cell = OptionCell::from_mut(&mut opt);
245            assert_eq!(cell.get(), None);
246            assert!(cell.set(43).is_ok());
247            assert_eq!(opt, Some(43));
248        }
249    }
250
251    #[test]
252    fn test_from_mut_slice() {
253        let mut opts = vec![Some(42), None, Some(43)];
254        let cells = OptionCell::from_mut_slice(&mut opts);
255        let cells_ref1 = &*cells;
256        let cells_ref2 = &*cells;
257        let cells_ref3 = &*cells;
258        assert_eq!(cells_ref1.len(), 3);
259        assert_eq!(cells_ref1[0].get(), Some(&42));
260        assert_eq!(cells_ref1[1].get(), None);
261        assert_eq!(cells_ref1[2].get(), Some(&43));
262
263        assert!(cells_ref2[1].set(44).is_ok());
264        assert_eq!(cells_ref3[1].get(), Some(&44));
265    }
266}