//! Compact representation of `Option` for types with a reserved value. //! //! Small Cranelift types like the 32-bit entity references are often used in tables and linked //! lists where an `Option` is needed. Unfortunately, that would double the size of the tables //! because `Option` is twice as big as `T`. //! //! This module provides a `PackedOption` for types that have a reserved value that can be used //! to represent `None`. use core::fmt; use core::mem; /// Types that have a reserved value which can't be created any other way. pub trait ReservedValue: Eq { /// Create an instance of the reserved value. fn reserved_value() -> Self; } /// Packed representation of `Option`. #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct PackedOption(T); impl PackedOption { /// Returns `true` if the packed option is a `None` value. pub fn is_none(&self) -> bool { self.0 == T::reserved_value() } /// Returns `true` if the packed option is a `Some` value. pub fn is_some(&self) -> bool { self.0 != T::reserved_value() } /// Expand the packed option into a normal `Option`. pub fn expand(self) -> Option { if self.is_none() { None } else { Some(self.0) } } /// Maps a `PackedOption` to `Option` by applying a function to a contained value. pub fn map(self, f: F) -> Option where F: FnOnce(T) -> U, { self.expand().map(f) } /// Unwrap a packed `Some` value or panic. pub fn unwrap(self) -> T { self.expand().unwrap() } /// Unwrap a packed `Some` value or panic. pub fn expect(self, msg: &str) -> T { self.expand().expect(msg) } /// Takes the value out of the packed option, leaving a `None` in its place. pub fn take(&mut self) -> Option { mem::replace(self, None.into()).expand() } } impl Default for PackedOption { /// Create a default packed option representing `None`. fn default() -> Self { PackedOption(T::reserved_value()) } } impl From for PackedOption { /// Convert `t` into a packed `Some(x)`. fn from(t: T) -> Self { debug_assert!( t != T::reserved_value(), "Can't make a PackedOption from the reserved value." ); PackedOption(t) } } impl From> for PackedOption { /// Convert an option into its packed equivalent. fn from(opt: Option) -> Self { match opt { None => Self::default(), Some(t) => t.into(), } } } impl Into> for PackedOption { fn into(self) -> Option { self.expand() } } impl fmt::Debug for PackedOption where T: ReservedValue + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.is_none() { write!(f, "None") } else { write!(f, "Some({:?})", self.0) } } } #[cfg(test)] mod tests { use super::*; // Dummy entity class, with no Copy or Clone. #[derive(Debug, PartialEq, Eq)] struct NoC(u32); impl ReservedValue for NoC { fn reserved_value() -> Self { NoC(13) } } #[test] fn moves() { let x = NoC(3); let somex: PackedOption = x.into(); assert!(!somex.is_none()); assert_eq!(somex.expand(), Some(NoC(3))); let none: PackedOption = None.into(); assert!(none.is_none()); assert_eq!(none.expand(), None); } // Dummy entity class, with Copy. #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Ent(u32); impl ReservedValue for Ent { fn reserved_value() -> Self { Ent(13) } } #[test] fn copies() { let x = Ent(2); let some: PackedOption = x.into(); assert_eq!(some.expand(), x.into()); assert_eq!(some, x.into()); } }