diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-04-30 20:34:41 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-05-07 08:13:24 -0700 |
| commit | 85a8e6b80ae91eb00a0bce96a5cec8017e1b37da (patch) | |
| tree | 832336fdfbad8e862d1b21b35b11774c88a1493b /src/libcore/finally.rs | |
| parent | 71924525458e508be139d76f48b34e64a5a9dca3 (diff) | |
| download | rust-85a8e6b80ae91eb00a0bce96a5cec8017e1b37da.tar.gz rust-85a8e6b80ae91eb00a0bce96a5cec8017e1b37da.zip | |
core: Inherit the finally module
Diffstat (limited to 'src/libcore/finally.rs')
| -rw-r--r-- | src/libcore/finally.rs | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/libcore/finally.rs b/src/libcore/finally.rs new file mode 100644 index 00000000000..6d02e6e3a17 --- /dev/null +++ b/src/libcore/finally.rs @@ -0,0 +1,165 @@ +// 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::unstable::finally::Finally; + +(|| { + // ... +}).finally(|| { + // this code is always run +}) +``` +*/ + +#![experimental] + +use ops::Drop; + +#[cfg(test)] use task::failing; + +/// 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::unstable::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); + } +} + +#[test] +fn test_success() { + let mut i = 0; + 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 = 0; + 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); +} |
