lateinit/
lib.rs

1#![no_std]
2
3#![feature(negative_impls)]
4
5//! Provides an unsafe way to late-initialize static variables that will see a lot of use.
6//! Meant as a replacement for `static mut` that only allows setting once.
7//!
8//! Usage:
9//!
10//! ```
11//! # use lateinit::LateInit;
12//! static SOMETHING: LateInit<String> = LateInit::new();
13//!
14//! unsafe { SOMETHING.init("hello world".to_owned()); }
15//! println!("{}", SOMETHING);
16//! ```
17//!
18//! Multiple-initialization causes a panic:
19//! ```should_panic
20//! # use lateinit::LateInit;
21//! static SOMETHING: LateInit<String> = LateInit::new();
22//!
23//! unsafe {
24//!     SOMETHING.init("something".to_owned());
25//!     SOMETHING.init("something else".to_owned());
26//! }
27//! ```
28
29use core::{
30    ops::Deref,
31    cmp::{
32        PartialEq,
33        PartialOrd,
34        Ordering
35    },
36    cell::UnsafeCell,
37    clone::Clone,
38    convert::AsRef,
39    fmt::{
40        Display,
41        Debug,
42        Formatter,
43        Error as FmtError
44    }
45};
46
47/// The primary type for this crate. Initialize before use.
48// We use UnsafeCell because we need interior mutability, and we're not using Cell because we don't
49//  want any runtime cost. There isn't any principled reason this is UnsafeCell<Option> rather than
50//  Option<UnsafeCell>, so if performance is better one way or the other this may change.
51pub struct LateInit<T>(UnsafeCell<Option<T>>);
52
53unsafe impl <T> Sync for LateInit<T> {}
54impl <T> !Send for LateInit<T> {}
55
56impl <T> LateInit<T> {
57    /// Create a new LateInit.
58    pub const fn new() -> Self {
59        LateInit(UnsafeCell::new(None))
60    }
61
62    /// Assign a value. Panics if called more than once.
63    pub unsafe fn init(&self, value: T) {
64        assert!(self.option().is_none(), "LateInit.init called more than once");
65
66        *self.0.get() = Some(value);
67    }
68
69    #[inline(always)]
70    fn option(&self) -> &Option<T> {
71        unsafe { &*self.0.get() }
72    }
73
74    #[inline(always)]
75    fn data(&self) -> &T {
76        #[cfg(not(feature = "debug_unchecked"))] {
77            debug_assert!(self.option().is_some(), "LateInit used without initialization");
78        }
79
80        match self.option() {
81            Some(ref x) => x,
82            _ => unreachable!(),
83        }
84    }
85}
86
87impl <T: Clone> LateInit<T> {
88    /// Clone contained value. Panics in debug profile if called before initialization.
89    ///
90    /// Note that `Clone` is not implemented because `LateInit` doesn't
91    /// support mutation, so `clone_from` is impossible.
92    #[inline(always)]
93    pub fn clone(&self) -> T {
94        self.data().clone()
95    }
96}
97
98impl <T> Deref for LateInit<T> {
99    type Target = T;
100
101    /// Deref to contained value. Panics in debug if called before initialization.
102    #[inline(always)]
103    fn deref(&self) -> &T {
104        self.data()
105    }
106}
107
108impl <T> AsRef<T> for LateInit<T> {
109    /// Panics in debug if called before initialization.
110    #[inline(always)]
111    fn as_ref(&self) -> &T {
112        self.data()
113    }
114}
115
116impl <T: PartialEq<W>, W> PartialEq<W> for LateInit<T> {
117    #[inline(always)]
118    fn eq(&self, other: &W) -> bool {
119        self.data().eq(other)
120    }
121
122    #[inline(always)]
123    fn ne(&self, other: &W) -> bool {
124        self.data().ne(other)
125    }
126}
127
128impl <T: PartialOrd<W>, W> PartialOrd<W> for LateInit<T> {
129    fn partial_cmp(&self, other: &W) -> Option<Ordering> {
130        self.data().partial_cmp(other)
131    }
132
133    fn lt(&self, other: &W) -> bool {
134        self.data().lt(other)
135    }
136
137    fn le(&self, other: &W) -> bool {
138        self.data().le(other)
139    }
140
141    fn gt(&self, other: &W) -> bool {
142        self.data().gt(other)
143    }
144
145    fn ge(&self, other: &W) -> bool {
146        self.data().ge(other)
147    }
148}
149
150impl <T: Debug> Debug for LateInit<T> {
151    /// Delegates to `Debug` implementation on contained value. This is a checked access.
152    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
153        match self.option() {
154            Some(ref x) => { x.fmt(f) },
155            None => { write!(f, "<UNINITIALIZED>") },
156        }
157    }
158}
159
160impl <T: Display> Display for LateInit<T> {
161    /// Delegates to `Display` implementation on contained value. This is a checked access.
162    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
163        match self.option() {
164            Some(ref x) => { x.fmt(f) },
165            None => { write!(f, "<UNINITIALIZED>") },
166        }
167    }
168}
169
170#[cfg(test)]
171mod test {
172    use super::*;
173
174    use core::convert::AsRef;
175    use core::ops::Deref;
176
177    #[test]
178    #[should_panic]
179    #[cfg(not(feature = "debug_unchecked"))]
180    fn multiple_init_panics() {
181        let li = LateInit::<usize>::new();
182        unsafe {
183            li.init(4);
184            li.init(4);
185        }
186    }
187
188    #[test]
189    #[should_panic]
190    #[cfg(not(feature = "debug_unchecked"))]
191    fn as_ref_panics() {
192        let li = LateInit::<usize>::new();
193        let _ = li.as_ref();
194    }
195
196    #[test]
197    #[should_panic]
198    #[cfg(not(feature = "debug_unchecked"))]
199    fn deref_panics() {
200        let li = LateInit::<usize>::new();
201        let _ = li.deref();
202    }
203
204    #[test]
205    fn compare() {
206        let li = LateInit::<usize>::new();
207        unsafe { li.init(4); }
208
209        assert!(li > 3);
210        assert!(li < 5);
211        assert!(li >= 4);
212        assert!(li <= 4);
213    }
214
215    #[test]
216    #[should_panic]
217    #[cfg(not(feature = "debug_unchecked"))]
218    fn compare_panics() {
219        let li = LateInit::<usize>::new();
220        let _ = li > 4;
221    }
222
223    #[test]
224    fn eq() {
225        let li = LateInit::<usize>::new();
226        unsafe { li.init(4); }
227
228        assert_eq!(li, 4);
229        assert_ne!(li, 5);
230    }
231
232    #[test]
233    #[should_panic]
234    #[cfg(not(feature = "debug_unchecked"))]
235    fn eq_panics() {
236        let li = LateInit::<usize>::new();
237        let _ = li == 4;
238    }
239}