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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. /*! The Finally trait provides a method, `finally` on stack closures that emulates Java-style try/finally blocks. Using the `finally` method is sometimes convenient, but the type rules prohibit any shared, mutable state between the "try" case and the "finally" case. For advanced cases, the `try_finally` function can also be used. See that function for more details. # Example ``` use std::finally::Finally; (|| { // ... }).finally(|| { // this code is always run }) ``` */ #![experimental] use ops::Drop; /// A trait for executing a destructor unconditionally after a block of code, /// regardless of whether the blocked fails. pub trait Finally<T> { /// Executes this object, unconditionally running `dtor` after this block of /// code has run. fn finally(&mut self, dtor: ||) -> T; } impl<'a,T> Finally<T> for ||: 'a -> T { fn finally(&mut self, dtor: ||) -> T { try_finally(&mut (), self, |_, f| (*f)(), |_| dtor()) } } impl<T> Finally<T> for fn() -> T { fn finally(&mut self, dtor: ||) -> T { try_finally(&mut (), (), |_, _| (*self)(), |_| dtor()) } } /** * The most general form of the `finally` functions. The function * `try_fn` will be invoked first; whether or not it fails, the * function `finally_fn` will be invoked next. The two parameters * `mutate` and `drop` are used to thread state through the two * closures. `mutate` is used for any shared, mutable state that both * closures require access to; `drop` is used for any state that the * `try_fn` requires ownership of. * * **WARNING:** While shared, mutable state between the try and finally * function is often necessary, one must be very careful; the `try` * function could have failed at any point, so the values of the shared * state may be inconsistent. * * # Example * * ``` * use std::finally::try_finally; * * struct State<'a> { buffer: &'a mut [u8], len: uint } * # let mut buf = []; * let mut state = State { buffer: buf, len: 0 }; * try_finally( * &mut state, (), * |state, ()| { * // use state.buffer, state.len * }, * |state| { * // use state.buffer, state.len to cleanup * }) * ``` */ pub fn try_finally<T,U,R>(mutate: &mut T, drop: U, try_fn: |&mut T, U| -> R, finally_fn: |&mut T|) -> R { let f = Finallyalizer { mutate: mutate, dtor: finally_fn, }; try_fn(&mut *f.mutate, drop) } struct Finallyalizer<'a,A> { mutate: &'a mut A, dtor: |&mut A|: 'a } #[unsafe_destructor] impl<'a,A> Drop for Finallyalizer<'a,A> { #[inline] fn drop(&mut self) { (self.dtor)(self.mutate); } } #[cfg(test)] mod test { use super::{try_finally, Finally}; use realstd::task::failing; #[test] fn test_success() { let mut i = 0i; try_finally( &mut i, (), |i, ()| { *i = 10; }, |i| { assert!(!failing()); assert_eq!(*i, 10); *i = 20; }); assert_eq!(i, 20); } #[test] #[should_fail] fn test_fail() { let mut i = 0i; try_finally( &mut i, (), |i, ()| { *i = 10; fail!(); }, |i| { assert!(failing()); assert_eq!(*i, 10); }) } #[test] fn test_retval() { let mut closure: || -> int = || 10; let i = closure.finally(|| { }); assert_eq!(i, 10); } #[test] fn test_compact() { fn do_some_fallible_work() {} fn but_always_run_this_function() { } let mut f = do_some_fallible_work; f.finally(but_always_run_this_function); } }