diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 1c1df691a52..54c0d98c798 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -277,7 +277,7 @@ use str::{StrSlice, OwnedStr}; use str; use to_str::ToStr; use uint; -use unstable::finally::Finally; +use unstable::finally::try_finally; use vec::{OwnedVector, MutableVector, ImmutableVector, OwnedCloneableVector}; use vec; @@ -473,25 +473,33 @@ pub trait Reader { /// pushed on to the vector, otherwise the amount `len` bytes couldn't be /// read (an error was encountered), and the error is returned. fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> IoResult<()> { + struct State<'a> { + buf: &'a mut ~[u8], + total_read: uint + } + let start_len = buf.len(); - let mut total_read = 0; + let mut s = State { buf: buf, total_read: 0 }; - buf.reserve_additional(len); - unsafe { buf.set_len(start_len + len); } + s.buf.reserve_additional(len); + unsafe { s.buf.set_len(start_len + len); } - (|| { - while total_read < len { - let len = buf.len(); - let slice = buf.mut_slice(start_len + total_read, len); - match self.read(slice) { - Ok(nread) => { - total_read += nread; + try_finally( + &mut s, (), + |s, _| { + while s.total_read < len { + let len = s.buf.len(); + let slice = s.buf.mut_slice(start_len + s.total_read, len); + match self.read(slice) { + Ok(nread) => { + s.total_read += nread; + } + Err(e) => return Err(e) } - Err(e) => return Err(e) } - } - Ok(()) - }).finally(|| unsafe { buf.set_len(start_len + total_read) }) + Ok(()) + }, + |s| unsafe { s.buf.set_len(start_len + s.total_read) }) } /// Reads `len` bytes and gives you back a new vector of length `len` diff --git a/src/libstd/unstable/finally.rs b/src/libstd/unstable/finally.rs index 6faf69d2bb9..433accecdbc 100644 --- a/src/libstd/unstable/finally.rs +++ b/src/libstd/unstable/finally.rs @@ -12,6 +12,11 @@ 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 ``` @@ -31,53 +36,89 @@ pub trait Finally { fn finally(&self, dtor: ||) -> T; } -macro_rules! finally_fn { - ($fnty:ty) => { - impl Finally for $fnty { - fn finally(&self, dtor: ||) -> T { - let _d = Finallyalizer { - dtor: dtor - }; - (*self)() - } - } - } -} - impl<'a,T> Finally for 'a || -> T { fn finally(&self, dtor: ||) -> T { - let _d = Finallyalizer { - dtor: dtor - }; - - (*self)() + try_finally(&mut (), (), + |_, _| (*self)(), + |_| dtor()) } } -finally_fn!(extern "Rust" fn() -> T) +impl Finally for fn() -> T { + fn finally(&self, dtor: ||) -> T { + try_finally(&mut (), (), + |_, _| (*self)(), + |_| dtor()) + } +} -struct Finallyalizer<'a> { - dtor: 'a || +/** + * 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 + * + * ``` + * struct State<'a> { buffer: &'a mut [u8], len: uint } + * 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(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: 'a |&mut A| } #[unsafe_destructor] -impl<'a> Drop for Finallyalizer<'a> { +impl<'a,A> Drop for Finallyalizer<'a,A> { #[inline] fn drop(&mut self) { - (self.dtor)(); + (self.dtor)(self.mutate); } } #[test] fn test_success() { let mut i = 0; - (|| { - i = 10; - }).finally(|| { - assert!(!failing()); - assert_eq!(i, 10); - i = 20; - }); + try_finally( + &mut i, (), + |i, ()| { + *i = 10; + }, + |i| { + assert!(!failing()); + assert_eq!(*i, 10); + *i = 20; + }); assert_eq!(i, 20); } @@ -85,13 +126,16 @@ fn test_success() { #[should_fail] fn test_fail() { let mut i = 0; - (|| { - i = 10; - fail!(); - }).finally(|| { - assert!(failing()); - assert_eq!(i, 10); - }) + try_finally( + &mut i, (), + |i, ()| { + *i = 10; + fail!(); + }, + |i| { + assert!(failing()); + assert_eq!(*i, 10); + }) } #[test] diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index b58e0820cfd..bbb11d774b0 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -119,7 +119,8 @@ use mem; use mem::size_of; use kinds::marker; use uint; -use unstable::finally::Finally; +use unstable::finally::try_finally; +use unstable::intrinsics; use unstable::raw::{Repr, Slice, Vec}; /** @@ -132,15 +133,16 @@ pub fn from_fn(n_elts: uint, op: |uint| -> T) -> ~[T] { unsafe { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); - let mut i: uint = 0u; - (|| { - while i < n_elts { - mem::move_val_init(&mut(*ptr::mut_offset(p, i as int)), op(i)); - i += 1u; - } - }).finally(|| { - v.set_len(i); - }); + let mut i = 0; + try_finally( + &mut i, (), + |i, ()| while *i < n_elts { + mem::move_val_init( + &mut(*ptr::mut_offset(p, *i as int)), + op(*i)); + *i += 1u; + }, + |i| v.set_len(*i)); v } } @@ -160,14 +162,15 @@ pub fn from_elem(n_elts: uint, t: T) -> ~[T] { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); let mut i = 0u; - (|| { - while i < n_elts { - mem::move_val_init(&mut(*ptr::mut_offset(p, i as int)), t.clone()); - i += 1u; - } - }).finally(|| { - v.set_len(i); - }); + try_finally( + &mut i, (), + |i, ()| while *i < n_elts { + mem::move_val_init( + &mut(*ptr::mut_offset(p, *i as int)), + t.clone()); + *i += 1u; + }, + |i| v.set_len(*i)); v } }