use std::panic;
pub mod scoped;
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);
}
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);
}