take_mut/
lib.rs

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