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