1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! This crate provides several functions for handling `&mut T` including `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 aborted, as there's no valid `T` to put back into the `&mut T`.
//! Use `take_or_recover()` to replace the `&mut T` with a recovery value before continuing the panic.
//!
//! 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`.
use std::panic;
pub mod scoped;
/// Allows use of a value pointed to by `&mut T` as though it was owned, as long as a `T` is made available afterwards.
///
/// The closure must return a valid T.
/// # Important
/// Will abort the program if the closure panics.
///
/// # Example
/// ```
/// struct Foo;
/// let mut foo = Foo;
/// take_mut::take(&mut foo, |foo| {
/// // Can now consume the Foo, and provide a new value later
/// drop(foo);
/// // Do more stuff
/// Foo // Return new Foo from closure, which goes back into the &mut Foo
/// });
/// ```
pub fn take<T, F>(mut_ref: &mut T, closure: F)
where F: FnOnce(T) -> T {
use std::ptr;
unsafe {
let old_t = ptr::read(mut_ref);
let new_t = panic::catch_unwind(panic::AssertUnwindSafe(|| closure(old_t)))
.unwrap_or_else(|_| ::std::process::abort());
ptr::write(mut_ref, new_t);
}
}
#[test]
fn it_works() {
#[derive(PartialEq, Eq, Debug)]
enum Foo {A, B};
impl Drop for Foo {
fn drop(&mut self) {
match *self {
Foo::A => println!("Foo::A dropped"),
Foo::B => println!("Foo::B dropped")
}
}
}
let mut foo = Foo::A;
take(&mut foo, |f| {
drop(f);
Foo::B
});
assert_eq!(&foo, &Foo::B);
}
/// Allows use of a value pointed to by `&mut T` as though it was owned, as long as a `T` is made available afterwards.
///
/// The closure must return a valid T.
/// # Important
/// Will replace `&mut T` with `recover` if the closure panics, then continues the panic.
///
/// # Example
/// ```
/// struct Foo;
/// let mut foo = Foo;
/// take_mut::take_or_recover(&mut foo, || Foo, |foo| {
/// // Can now consume the Foo, and provide a new value later
/// drop(foo);
/// // Do more stuff
/// Foo // Return new Foo from closure, which goes back into the &mut Foo
/// });
/// ```
pub fn take_or_recover<T, F, R>(mut_ref: &mut T, recover: R, closure: F)
where F: FnOnce(T) -> T, R: FnOnce() -> T {
use std::ptr;
unsafe {
let old_t = ptr::read(mut_ref);
let new_t = panic::catch_unwind(panic::AssertUnwindSafe(|| closure(old_t)));
match new_t {
Err(err) => {
let r = panic::catch_unwind(panic::AssertUnwindSafe(|| recover()))
.unwrap_or_else(|_| ::std::process::abort());
ptr::write(mut_ref, r);
panic::resume_unwind(err);
}
Ok(new_t) => ptr::write(mut_ref, new_t),
}
}
}
#[test]
fn it_works_recover() {
#[derive(PartialEq, Eq, Debug)]
enum Foo {A, B};
impl Drop for Foo {
fn drop(&mut self) {
match *self {
Foo::A => println!("Foo::A dropped"),
Foo::B => println!("Foo::B dropped")
}
}
}
let mut foo = Foo::A;
take_or_recover(&mut foo, || Foo::A, |f| {
drop(f);
Foo::B
});
assert_eq!(&foo, &Foo::B);
}
#[test]
fn it_works_recover_panic() {
#[derive(PartialEq, Eq, Debug)]
enum Foo {A, B, C};
impl Drop for Foo {
fn drop(&mut self) {
match *self {
Foo::A => println!("Foo::A dropped"),
Foo::B => println!("Foo::B dropped"),
Foo::C => println!("Foo::C dropped")
}
}
}
let mut foo = Foo::A;
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
take_or_recover(&mut foo, || Foo::C, |f| {
drop(f);
panic!("panic");
Foo::B
});
}));
assert!(res.is_err());
assert_eq!(&foo, &Foo::C);
}