#![feature(untagged_unions, const_fn)]
#![no_std]
use core::mem::replace;
#[allow(unions_with_drop_fields)]
pub union UntaggedOption<T> {
pub some: T,
pub none: (),
}
impl<T> UntaggedOption<T> {
pub const fn none() -> Self {
UntaggedOption {
none: (),
}
}
pub const fn some(t: T) -> Self {
UntaggedOption {
some: t,
}
}
pub unsafe fn take(&mut self) -> T {
replace(self, UntaggedOption::none()).some
}
pub unsafe fn as_ref(&self) -> &T {
&self.some
}
pub unsafe fn as_mut(&mut self) -> &mut T {
&mut self.some
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn static_context() {
static mut MY_OPT: UntaggedOption<u8> = UntaggedOption::none();
unsafe {
MY_OPT = UntaggedOption::some(123);
assert_eq!(*MY_OPT.as_ref(), 123);
*MY_OPT.as_mut() = 42;
assert_eq!(*MY_OPT.as_ref(), 42);
MY_OPT.take();
}
}
#[test]
fn correct_drop() {
use core::sync::atomic::{AtomicUsize, Ordering};
static DROPCOUNT: AtomicUsize = AtomicUsize::new(0);
struct MyDrop;
impl Drop for MyDrop {
fn drop(&mut self) {
DROPCOUNT.fetch_add(1, Ordering::SeqCst);
}
}
let mut opt = UntaggedOption::some(MyDrop);
drop(opt);
assert_eq!(DROPCOUNT.load(Ordering::SeqCst), 0);
opt = UntaggedOption::some(MyDrop);
assert_eq!(DROPCOUNT.load(Ordering::SeqCst), 0);
unsafe { opt.take(); }
assert_eq!(DROPCOUNT.load(Ordering::SeqCst), 1);
}
}