1#![warn(missing_docs)]
31
32
33use std;
34use std::panic;
35use std::cell::Cell;
36use std::marker::PhantomData;
37
38pub struct Scope<'s> {
40 active_holes: Cell<usize>,
41 marker: PhantomData<Cell<&'s mut ()>>
42}
43
44impl<'s> Scope<'s> {
45
46 pub fn take_or_recover<'c, 'm: 's, T: 'm, F: FnOnce() -> T>(&'c self, mut_ref: &'m mut T, recovery: F) -> (T, Hole<'c, 'm, T, F>) {
50 use std::ptr;
51
52 let t: T;
53 let hole: Hole<'c, 'm, T, F>;
54 let num_of_holes = self.active_holes.get();
55 if num_of_holes == std::usize::MAX {
56 panic!("Too many holes!");
57 }
58 self.active_holes.set(num_of_holes + 1);
59 unsafe {
60 t = ptr::read(mut_ref as *mut T);
61 hole = Hole {
62 active_holes: &self.active_holes,
63 hole: mut_ref as *mut T,
64 phantom: PhantomData,
65 recovery: Some(recovery)
66 };
67 };
68 (t, hole)
69 }
70
71 pub fn take<'c, 'm: 's, T: 'm>(&'c self, mut_ref: &'m mut T) -> (T, Hole<'c, 'm, T, fn() -> T>) {
73 #[allow(missing_docs)]
74 fn panic<T>() -> T {
75 panic!("Failed to recover a Hole!")
76 }
77 self.take_or_recover(mut_ref, panic)
78 }
79}
80
81pub fn scope<'s, F, R>(f: F) -> R
85 where F: FnOnce(&Scope<'s>) -> R {
86 let this = Scope { active_holes: Cell::new(0), marker: PhantomData };
87 let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
88 f(&this)
89 }));
90 if this.active_holes.get() != 0 {
91 std::process::abort();
92 }
93 match result {
94 Ok(r) => r,
95 Err(p) => panic::resume_unwind(p),
96 }
97
98}
99
100#[must_use]
106pub struct Hole<'c, 'm, T: 'm, F: FnOnce() -> T> {
107 active_holes: &'c Cell<usize>,
108 hole: *mut T,
109 phantom: PhantomData<&'m mut T>,
110 recovery: Option<F>,
111}
112
113impl<'c, 'm, T: 'm, F: FnOnce() -> T> Hole<'c, 'm, T, F> {
114 pub fn fill(self, t: T) {
116 use std::ptr;
117 use std::mem;
118
119 unsafe {
120 ptr::write(self.hole, t);
121 }
122 let num_holes = self.active_holes.get();
123 self.active_holes.set(num_holes - 1);
124 mem::forget(self);
125 }
126}
127
128impl<'c, 'm, T: 'm, F: FnOnce() -> T> Drop for Hole<'c, 'm, T, F> {
129 fn drop(&mut self) {
130 use std::ptr;
131
132 let t = (self.recovery.take().expect("No recovery function in Hole!"))();
133 unsafe {
134 ptr::write(self.hole, t);
135 }
136 let num_holes = self.active_holes.get();
137 self.active_holes.set(num_holes - 1);
138 }
139}
140
141#[test]
142fn scope_based_take() {
143 #[derive(Debug)]
144 struct Foo;
145
146 #[derive(Debug)]
147 struct Bar {
148 a: Foo,
149 b: Foo
150 }
151 let mut bar = Bar { a: Foo, b: Foo };
152 scope(|scope| {
153 let (a, a_hole) = scope.take(&mut bar.a);
154 let (b, b_hole) = scope.take(&mut bar.b);
155 a_hole.fill(Foo);
157 b_hole.fill(Foo);
158 });
159 println!("{:?}", &bar);
160}
161
162#[test]
163fn panic_on_recovered_panic() {
164 use std::panic;
165
166 struct Foo;
167 let mut foo = Foo;
168 let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
169 scope(|scope| {
170 let (t, hole) = scope.take_or_recover(&mut foo, || Foo);
171 panic!("Oops!");
172 });
173 }));
174 assert!(result.is_err());
175}