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}