use core::{
fmt::Display,
ops::{Deref, DerefMut},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Takeable<T> {
value: Option<T>,
}
const PANIC_MESSAGE: &str = "the value has already been removed from the Takeable";
impl<T> Takeable<T> {
#[inline(always)]
pub fn new(value: T) -> Takeable<T> {
Takeable { value: Some(value) }
}
#[inline(always)]
#[track_caller]
pub fn into_inner(self) -> T {
self.value.expect(PANIC_MESSAGE)
}
#[inline(always)]
#[track_caller]
pub fn borrow<F>(&mut self, f: F)
where
F: FnOnce(T) -> T,
{
self.borrow_result(|v| (f(v), ()))
}
#[inline(always)]
#[track_caller]
pub fn borrow_result<F, R>(&mut self, f: F) -> R
where
F: FnOnce(T) -> (T, R),
{
let old = self.value.take().expect(PANIC_MESSAGE);
let (new, result) = f(old);
self.value = Some(new);
result
}
#[inline(always)]
#[track_caller]
pub fn take(&mut self) -> T {
self.value.take().expect(PANIC_MESSAGE)
}
#[inline(always)]
pub fn is_usable(&self) -> bool {
self.value.is_some()
}
}
impl<T: Display> Display for Takeable<T> {
#[inline(always)]
#[track_caller]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl<T> Deref for Takeable<T> {
type Target = T;
#[inline(always)]
#[track_caller]
fn deref(&self) -> &T {
self.as_ref()
}
}
impl<T> DerefMut for Takeable<T> {
#[inline(always)]
#[track_caller]
fn deref_mut(&mut self) -> &mut T {
self.as_mut()
}
}
impl<T> From<T> for Takeable<T> {
#[inline(always)]
#[track_caller]
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T> AsRef<T> for Takeable<T> {
#[inline(always)]
#[track_caller]
fn as_ref(&self) -> &T {
self.value.as_ref().expect(PANIC_MESSAGE)
}
}
impl<T> AsMut<T> for Takeable<T> {
#[inline(always)]
#[track_caller]
fn as_mut(&mut self) -> &mut T {
self.value.as_mut().expect(PANIC_MESSAGE)
}
}
#[cfg(test)]
mod tests {
use super::Takeable;
#[test]
fn test_takeable() {
let mut takeable = Takeable::new(42u32);
*takeable += 1;
assert_eq!(*takeable, 43);
*takeable.as_mut() += 1;
assert_eq!(takeable.as_ref(), &44);
takeable.borrow(|n: u32| n + 1);
assert_eq!(*takeable, 45);
let out = takeable.borrow_result(|n: u32| (n + 1, n));
assert_eq!(out, 45);
assert_eq!(takeable.into_inner(), 46);
let mut takeable = Takeable::new(34u32);
assert_eq!(takeable.take(), 34);
}
#[test]
#[should_panic]
fn test_usable() {
struct MyDrop {
value: Takeable<()>,
should_be_usable: bool,
}
impl Drop for MyDrop {
fn drop(&mut self) {
assert_eq!(self.value.is_usable(), self.should_be_usable);
}
}
let _drop1 = MyDrop {
value: Takeable::new(()),
should_be_usable: true,
};
let mut drop2 = MyDrop {
value: Takeable::new(()),
should_be_usable: false,
};
let mut drop3 = MyDrop {
value: Takeable::new(()),
should_be_usable: false,
};
let _ = drop3.value.take();
drop2.value.borrow(|_| panic!());
}
}