diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2013-09-17 19:42:07 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-09-17 20:50:23 -0700 |
| commit | 88bc11e646c8d1db7ac7894f74e2f660d9a82c54 (patch) | |
| tree | e0dfde7157dadefae8bb2420e8e93f9eefc95bfa /src/libstd/condition.rs | |
| parent | 0efc4822e93221714aa7d142de44a057bdbad2ca (diff) | |
| download | rust-88bc11e646c8d1db7ac7894f74e2f660d9a82c54.tar.gz rust-88bc11e646c8d1db7ac7894f74e2f660d9a82c54.zip | |
Document a few undocumented modules in libstd
Hopefull this will make our libstd docs appear a little more "full".
Diffstat (limited to 'src/libstd/condition.rs')
| -rw-r--r-- | src/libstd/condition.rs | 175 |
1 files changed, 142 insertions, 33 deletions
diff --git a/src/libstd/condition.rs b/src/libstd/condition.rs index 954b8bd7330..c47dcfe3de6 100644 --- a/src/libstd/condition.rs +++ b/src/libstd/condition.rs @@ -8,71 +8,179 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Condition handling */ +/*! -#[allow(missing_doc)]; +Condition handling + +Conditions are a utility used to deal with handling error conditions. The syntax +of a condition handler strikes a resemblance to try/catch blocks in other +languages, but condition handlers are *not* a form of exception handling in the +same manner. + +A condition is declared through the `condition!` macro provided by the compiler: + +~~~{.rust} +condition! { + pub my_error: int -> ~str; +} +~~~ + +This macro declares an inner module called `my_error` with one static variable, +`cond` that is a static `Condition` instance. To help understand what the other +parameters are used for, an example usage of this condition would be: + +~~~{.rust} +do my_error::cond.trap(|raised_int| { + + // the condition `my_error` was raised on, and the value it raised is stored + // in `raised_int`. This closure must return a `~str` type (as specified in + // the declaration of the condition + if raised_int == 3 { ~"three" } else { ~"oh well" } + +}).inside { + + // The condition handler above is installed for the duration of this block. + // That handler will override any previous handler, but the previous handler + // is restored when this block returns (handlers nest) + // + // If any code from this block (or code from another block) raises on the + // condition, then the above handler will be invoked (so long as there's no + // other nested handler). + + println(my_error::cond.raise(3)); // prints "three" + println(my_error::cond.raise(4)); // prints "oh well" + +} +~~~ + +Condition handling is useful in cases where propagating errors is either to +cumbersome or just not necessary in the first place. It should also be noted, +though, that if there is not handler installed when a condition is raised, then +the task invokes `fail!()` and will terminate. + +## More Info + +Condition handlers as an error strategy is well explained in the [conditions +tutorial](http://static.rust-lang.org/doc/master/tutorial-conditions.html), +along with comparing and contrasting it with other error handling strategies. + +*/ use local_data; use prelude::*; +use unstable::raw::Closure; -// helper for transmutation, shown below. -type RustClosure = (int, int); - +#[doc(hidden)] pub struct Handler<T, U> { - handle: RustClosure, - prev: Option<@Handler<T, U>>, + priv handle: Closure, + priv prev: Option<@Handler<T, U>>, } +/// This struct represents the state of a condition handler. It contains a key +/// into TLS which holds the currently install handler, along with the name of +/// the condition (useful for debugging). +/// +/// This struct should never be created directly, but rather only through the +/// `condition!` macro provided to all libraries using libstd. pub struct Condition<T, U> { + /// Name of the condition handler name: &'static str, + /// TLS key used to insert/remove values in TLS. key: local_data::Key<@Handler<T, U>> } impl<T, U> Condition<T, U> { + /// Creates an object which binds the specified handler. This will also save + /// the current handler *on creation* such that when the `Trap` is consumed, + /// it knows which handler to restore. + /// + /// # Example + /// + /// ~~~{.rust} + /// condition! { my_error: int -> int; } + /// + /// let trap = my_error::cond.trap(|error| error + 3); + /// + /// // use `trap`'s inside method to register the handler and then run a + /// // block of code with the handler registered + /// ~~~ pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> { - unsafe { - let p : *RustClosure = ::cast::transmute(&h); - let prev = local_data::get(self.key, |k| k.map(|&x| *x)); - let h = @Handler { handle: *p, prev: prev }; - Trap { cond: self, handler: h } - } + let h: Closure = unsafe { ::cast::transmute(h) }; + let prev = local_data::get(self.key, |k| k.map(|&x| *x)); + let h = @Handler { handle: h, prev: prev }; + Trap { cond: self, handler: h } } + /// Raises on this condition, invoking any handler if one has been + /// registered, or failing the current task otherwise. + /// + /// While a condition handler is being run, the condition will have no + /// handler listed, so a task failure will occur if the condition is + /// re-raised during the handler. + /// + /// # Arguments + /// + /// * t - The argument to pass along to the condition handler. + /// + /// # Return value + /// + /// If a handler is found, its return value is returned, otherwise this + /// function will not return. pub fn raise(&self, t: T) -> U { let msg = fmt!("Unhandled condition: %s: %?", self.name, t); self.raise_default(t, || fail!(msg.clone())) } + /// Performs the same functionality as `raise`, except that when no handler + /// is found the `default` argument is called instead of failing the task. pub fn raise_default(&self, t: T, default: &fn() -> U) -> U { - unsafe { - match local_data::pop(self.key) { - None => { - debug!("Condition.raise: found no handler"); - default() - } - Some(handler) => { - debug!("Condition.raise: found handler"); - match handler.prev { - None => {} - Some(hp) => local_data::set(self.key, hp) - } - let handle : &fn(T) -> U = - ::cast::transmute(handler.handle); - let u = handle(t); - local_data::set(self.key, handler); - u + match local_data::pop(self.key) { + None => { + debug!("Condition.raise: found no handler"); + default() + } + Some(handler) => { + debug!("Condition.raise: found handler"); + match handler.prev { + None => {} + Some(hp) => local_data::set(self.key, hp) } + let handle : &fn(T) -> U = unsafe { + ::cast::transmute(handler.handle) + }; + let u = handle(t); + local_data::set(self.key, handler); + u } } } } +/// A `Trap` is created when the `trap` method is invoked on a `Condition`, and +/// it is used to actually bind a handler into the TLS slot reserved for this +/// condition. +/// +/// Normally this object is not dealt with directly, but rather it's directly +/// used after being returned from `trap` struct Trap<'self, T, U> { - cond: &'self Condition<T, U>, - handler: @Handler<T, U> + priv cond: &'self Condition<T, U>, + priv handler: @Handler<T, U> } impl<'self, T, U> Trap<'self, T, U> { + /// Execute a block of code with this trap handler's exception handler + /// registered. + /// + /// # Example + /// + /// ~~~{.rust} + /// condition! { my_error: int -> int; } + /// + /// let result = do my_error::cond.trap(|error| error + 3).inside { + /// my_error::cond.raise(4) + /// }; + /// assert_eq!(result, 7); + /// ~~~ pub fn inside<V>(&self, inner: &'self fn() -> V) -> V { let _g = Guard { cond: self.cond }; debug!("Trap: pushing handler to TLS"); @@ -81,8 +189,9 @@ impl<'self, T, U> Trap<'self, T, U> { } } +#[doc(hidden)] struct Guard<'self, T, U> { - cond: &'self Condition<T, U> + priv cond: &'self Condition<T, U> } #[unsafe_destructor] |
