Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[package]
name = "take_mut"
version = "0.1.3"
version = "0.1.4"
authors = ["Sgeo <sgeoster@gmail.com>"]
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/"
documentation = "https://crates.fyi/crates/take_mut/0.1.3/"

[dependencies]
unreachable="0.1.1"
46 changes: 44 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -39,6 +44,43 @@ pub fn take<T, F>(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<T> Sentinel for Option<T> {
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<T, F>(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() {
Expand All @@ -58,4 +100,4 @@ fn it_works() {
Foo::B
});
assert_eq!(&foo, &Foo::B);
}
}