takeable/
lib.rs

1// Copyright 2017 Mathias Svensson. See the COPYRIGHT file at the top-level
2// directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT> or the Unlicense
7// <LICENSE-UNLICENSE or https://unlicense.org/UNLICENSE>, at your option.
8// All files in the project carrying such notice may not be copied,
9// modified, or distributed except according to those terms.
10
11//! Crate for temporarily or permanently moving out of a mutable pointer.
12//!
13//! This crate implements a single wrapper-type [`Takeable<T>`]. The main purpose of this wrapper
14//! is that it provides two convenient helper functions [`Takeable::borrow`] and [`Takeable::borrow_result`]
15//! that allows for temporarily moving out of the wrapper without violating safety. The value can also be permanently
16//! moved out, invalidating the  container and causing a panic on any future attempts to access
17//! the value.
18//!
19//! The [`Takeable::borrow`] and [`Takeable::borrow_result`] methods work similarly to
20//! [`take`] from the [`take_mut`] crate. The main difference is that, while the [`take_mut`]
21//! is implemented using careful handling of unwind safety, this crate uses an [`Option<T>`] inside to make
22//! unwinding work as expected.
23//!
24//! The [`Takeable::take`] method works similarly to [`Option::take`], but has the advantage that the object becomes
25//! permanently invalidated. Additionally, using a [`Takeable<T>`] instead of an [`Option<T>`] makes
26//! it clear in code that a value is always expected and avoids the need to handle possible
27//! [`Option::None`] variants when accessing the `T`.
28//!
29//! [`take`]: https://docs.rs/take_mut/latest/take_mut/fn.take.html
30//! [`take_mut`]: https://crates.io/crates/take_mut
31#![no_std]
32#![deny(
33    missing_docs,
34    unsafe_code,
35    missing_debug_implementations,
36    missing_copy_implementations,
37    unstable_features,
38    unused_import_braces,
39    unused_qualifications
40)]
41
42use core::{
43    fmt::Display,
44    ops::{Deref, DerefMut},
45};
46
47/// A wrapper-type that always holds a single `T` value.
48///
49/// This type is implemented using an [`Option<T>`], however, the wrapper requires the [`Option<T>`] to
50/// always contain a value.
51///
52/// # Panics
53///
54/// If the closure given to [`borrow`][Self::borrow] or [`borrow_result`][Self::borrow_result] panics, then the `Takeable` is left in an
55/// invalid state without holding a `T`. Calling any method on the object besides [`is_usable`][Self::is_usable] when
56/// in this state will result in a panic. This includes trying to dereference the object. The object
57/// will also be permanently invalidated if the value is moved out manually using [`take`][Self::take].
58///
59/// It is always safe to drop the `Takeable` even when invalidated.
60#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
61pub struct Takeable<T> {
62    // During normal usage, the invariant is that that this value should *always* be a Some(value),
63    // unless we are inside the `borrow_result` function. However if the closure given the
64    // `borrow_result` panics, or the value is taken with `take`, then this will no longer be the
65    // case.
66    value: Option<T>,
67}
68
69/// Message to print when panicking because the `Takeable<T>` has been invalidated.
70const PANIC_MESSAGE: &str = "the value has already been removed from the Takeable";
71
72impl<T> Takeable<T> {
73    /// Constructs a new [`Takeable<T>`] value.
74    #[inline(always)]
75    pub fn new(value: T) -> Takeable<T> {
76        Takeable { value: Some(value) }
77    }
78
79    /// Takes ownership of the inner value.
80    #[inline(always)]
81    #[track_caller]
82    pub fn into_inner(self) -> T {
83        self.value.expect(PANIC_MESSAGE)
84    }
85
86    /// Updates the inner value using the provided closure.
87    #[inline(always)]
88    #[track_caller]
89    pub fn borrow<F>(&mut self, f: F)
90    where
91        F: FnOnce(T) -> T,
92    {
93        self.borrow_result(|v| (f(v), ()))
94    }
95
96    /// Updates the inner value using the provided closure, which also returns a result.
97    #[inline(always)]
98    #[track_caller]
99    pub fn borrow_result<F, R>(&mut self, f: F) -> R
100    where
101        F: FnOnce(T) -> (T, R),
102    {
103        let old = self.value.take().expect(PANIC_MESSAGE);
104        let (new, result) = f(old);
105        self.value = Some(new);
106        result
107    }
108
109    /// Moves out the inner value and invalidates the object.
110    ///
111    /// Subsequent calls to any methods except [`is_usable`][Self::is_usable] will panic, including attempts to
112    /// deference the object.
113    #[inline(always)]
114    #[track_caller]
115    pub fn take(&mut self) -> T {
116        self.value.take().expect(PANIC_MESSAGE)
117    }
118
119    /// Check if the object is still usable.
120    ///
121    /// The object will always start out as usable, and can only enter an unusable state if the
122    /// methods [`borrow`][Self::borrow] or [`borrow_result`][Self::borrow_result] are called with closures that panic, or if the value
123    /// is explicitly moved out permanently with [`take`][Self::take].
124    #[inline(always)]
125    pub fn is_usable(&self) -> bool {
126        self.value.is_some()
127    }
128}
129
130impl<T: Display> Display for Takeable<T> {
131    #[inline(always)]
132    #[track_caller]
133    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134        write!(f, "{}", self.as_ref())
135    }
136}
137
138impl<T> Deref for Takeable<T> {
139    type Target = T;
140    #[inline(always)]
141    #[track_caller]
142    fn deref(&self) -> &T {
143        self.as_ref()
144    }
145}
146
147impl<T> DerefMut for Takeable<T> {
148    #[inline(always)]
149    #[track_caller]
150    fn deref_mut(&mut self) -> &mut T {
151        self.as_mut()
152    }
153}
154
155impl<T> From<T> for Takeable<T> {
156    #[inline(always)]
157    #[track_caller]
158    /// Converts a `T` value into a [`Takeable<T>`].
159    fn from(value: T) -> Self {
160        Self::new(value)
161    }
162}
163
164impl<T> AsRef<T> for Takeable<T> {
165    #[inline(always)]
166    #[track_caller]
167    /// Gets a reference to the underlying value.
168    fn as_ref(&self) -> &T {
169        self.value.as_ref().expect(PANIC_MESSAGE)
170    }
171}
172
173impl<T> AsMut<T> for Takeable<T> {
174    #[inline(always)]
175    #[track_caller]
176    /// Gets a mutable reference to the underlying value.
177    fn as_mut(&mut self) -> &mut T {
178        self.value.as_mut().expect(PANIC_MESSAGE)
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::Takeable;
185    #[test]
186    fn test_takeable() {
187        let mut takeable = Takeable::new(42u32);
188        *takeable += 1;
189        assert_eq!(*takeable, 43);
190        *takeable.as_mut() += 1;
191        assert_eq!(takeable.as_ref(), &44);
192        takeable.borrow(|n: u32| n + 1);
193        assert_eq!(*takeable, 45);
194        let out = takeable.borrow_result(|n: u32| (n + 1, n));
195        assert_eq!(out, 45);
196        assert_eq!(takeable.into_inner(), 46);
197        let mut takeable = Takeable::new(34u32);
198        assert_eq!(takeable.take(), 34);
199    }
200
201    #[test]
202    #[should_panic]
203    fn test_usable() {
204        struct MyDrop {
205            value: Takeable<()>,
206            should_be_usable: bool,
207        }
208        impl Drop for MyDrop {
209            fn drop(&mut self) {
210                assert_eq!(self.value.is_usable(), self.should_be_usable);
211            }
212        }
213
214        let _drop1 = MyDrop {
215            value: Takeable::new(()),
216            should_be_usable: true,
217        };
218        let mut drop2 = MyDrop {
219            value: Takeable::new(()),
220            should_be_usable: false,
221        };
222        let mut drop3 = MyDrop {
223            value: Takeable::new(()),
224            should_be_usable: false,
225        };
226        let _ = drop3.value.take();
227        drop2.value.borrow(|_| panic!());
228    }
229}