diff --git a/Cargo.toml b/Cargo.toml index 08dbd2a..077c1fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "take_mut" -version = "0.1.3" +version = "0.1.4" authors = ["Sgeo "] license = "MIT" homepage = "https://github.com/Sgeo/take_mut" repository = "https://github.com/Sgeo/take_mut" description = "Take a T from a &mut T temporarily" -documentation = "https://crates.fyi/crates/take_mut/0.1.3/" \ No newline at end of file +documentation = "https://crates.fyi/crates/take_mut/0.1.3/" + +[dependencies] +unreachable="0.1.1" diff --git a/src/lib.rs b/src/lib.rs index 8babf8d..4075319 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,15 @@ -//! This crate provides (at this time) a single function, `take()`. +//! This crate provides function, `take()`. //! //! `take()` allows for taking `T` out of a `&mut T`, doing anything with it including consuming it, and producing another `T` to put back in the `&mut T`. //! //! During `take()`, if a panic occurs, the entire process will be exited, as there's no valid `T` to put back into the `&mut T`. //! //! Contrast with `std::mem::replace()`, which allows for putting a different `T` into a `&mut T`, but requiring the new `T` to be available before being able to consume the old `T`. +//! +//! The crate also provides `take_no_exit()` function, which behaves similarly but instead of exiting +//! the program on panic, it leaves a sentinel value there. + +extern crate unreachable; mod exit_on_panic; @@ -39,6 +44,43 @@ pub fn take(mut_ref: &mut T, closure: F) }); } +/// Represents an invalid value that is safe to drop +pub trait Sentinel: Sized { + /// Creates the sentinel. + fn new_sentinel() -> Self; + + /// Releases the sentinel. Calling this indicates that nothing unexpected happened. + /// The caller must make sure that the value this function is called with is the exact same + /// value the `new_sentinel()` funtion returned. + unsafe fn release_sentinel(self) { + } +} + +impl Sentinel for Option { + fn new_sentinel() -> Self { + None + } + + unsafe fn release_sentinel(self) { + // This avoids unnecessary check for None + use unreachable::UncheckedOptionExt; + self.unchecked_unwrap_none(); + } +} + +/// This function is similar to `take()` but instead of exiting, it will leave sentinel value in +/// place of the original in case of panic. +pub fn take_no_exit(mut_ref: &mut T, closure: F) + where T: Sentinel, + F: FnOnce(T) -> T { + use std::mem::replace; + unsafe { + let old_t = replace(mut_ref, Sentinel::new_sentinel()); + let new_t = closure(old_t); + replace(mut_ref, new_t).release_sentinel(); + } +} + #[test] fn it_works() { @@ -58,4 +100,4 @@ fn it_works() { Foo::B }); assert_eq!(&foo, &Foo::B); -} \ No newline at end of file +}