#![no_std]
#![feature(negative_impls)]
use core::{
ops::Deref,
cmp::{
PartialEq,
PartialOrd,
Ordering
},
cell::UnsafeCell,
clone::Clone,
convert::AsRef,
fmt::{
Display,
Debug,
Formatter,
Error as FmtError
}
};
pub struct LateInit<T>(UnsafeCell<Option<T>>);
unsafe impl <T> Sync for LateInit<T> {}
impl <T> !Send for LateInit<T> {}
impl <T> LateInit<T> {
pub const fn new() -> Self {
LateInit(UnsafeCell::new(None))
}
pub unsafe fn init(&self, value: T) {
assert!(self.option().is_none(), "LateInit.init called more than once");
*self.0.get() = Some(value);
}
#[inline(always)]
fn option(&self) -> &Option<T> {
unsafe { &*self.0.get() }
}
#[inline(always)]
fn data(&self) -> &T {
#[cfg(not(feature = "debug_unchecked"))] {
debug_assert!(self.option().is_some(), "LateInit used without initialization");
}
match self.option() {
Some(ref x) => x,
_ => unreachable!(),
}
}
}
impl <T: Clone> LateInit<T> {
#[inline(always)]
pub fn clone(&self) -> T {
self.data().clone()
}
}
impl <T> Deref for LateInit<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
self.data()
}
}
impl <T> AsRef<T> for LateInit<T> {
#[inline(always)]
fn as_ref(&self) -> &T {
self.data()
}
}
impl <T: PartialEq<W>, W> PartialEq<W> for LateInit<T> {
#[inline(always)]
fn eq(&self, other: &W) -> bool {
self.data().eq(other)
}
#[inline(always)]
fn ne(&self, other: &W) -> bool {
self.data().ne(other)
}
}
impl <T: PartialOrd<W>, W> PartialOrd<W> for LateInit<T> {
fn partial_cmp(&self, other: &W) -> Option<Ordering> {
self.data().partial_cmp(other)
}
fn lt(&self, other: &W) -> bool {
self.data().lt(other)
}
fn le(&self, other: &W) -> bool {
self.data().le(other)
}
fn gt(&self, other: &W) -> bool {
self.data().gt(other)
}
fn ge(&self, other: &W) -> bool {
self.data().ge(other)
}
}
impl <T: Debug> Debug for LateInit<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match self.option() {
Some(ref x) => { x.fmt(f) },
None => { write!(f, "<UNINITIALIZED>") },
}
}
}
impl <T: Display> Display for LateInit<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match self.option() {
Some(ref x) => { x.fmt(f) },
None => { write!(f, "<UNINITIALIZED>") },
}
}
}
#[cfg(test)]
mod test {
use super::*;
use core::convert::AsRef;
use core::ops::Deref;
#[test]
#[should_panic]
#[cfg(not(feature = "debug_unchecked"))]
fn multiple_init_panics() {
let li = LateInit::<usize>::new();
unsafe {
li.init(4);
li.init(4);
}
}
#[test]
#[should_panic]
#[cfg(not(feature = "debug_unchecked"))]
fn as_ref_panics() {
let li = LateInit::<usize>::new();
let _ = li.as_ref();
}
#[test]
#[should_panic]
#[cfg(not(feature = "debug_unchecked"))]
fn deref_panics() {
let li = LateInit::<usize>::new();
let _ = li.deref();
}
#[test]
fn compare() {
let li = LateInit::<usize>::new();
unsafe { li.init(4); }
assert!(li > 3);
assert!(li < 5);
assert!(li >= 4);
assert!(li <= 4);
}
#[test]
#[should_panic]
#[cfg(not(feature = "debug_unchecked"))]
fn compare_panics() {
let li = LateInit::<usize>::new();
let _ = li > 4;
}
#[test]
fn eq() {
let li = LateInit::<usize>::new();
unsafe { li.init(4); }
assert_eq!(li, 4);
assert_ne!(li, 5);
}
#[test]
#[should_panic]
#[cfg(not(feature = "debug_unchecked"))]
fn eq_panics() {
let li = LateInit::<usize>::new();
let _ = li == 4;
}
}