88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11- /*! Condition handling */
11+ /*!
1212
13- #[ allow( missing_doc) ] ;
13+ Condition handling
14+
15+ Conditions are a utility used to deal with handling error conditions. The syntax
16+ of a condition handler strikes a resemblance to try/catch blocks in other
17+ languages, but condition handlers are *not* a form of exception handling in the
18+ same manner.
19+
20+ A condition is declared through the `condition!` macro provided by the compiler:
21+
22+ ~~~{.rust}
23+ condition! {
24+ pub my_error: int -> ~str;
25+ }
26+ ~~~
27+
28+ This macro declares an inner module called `my_error` with one static variable,
29+ `cond` that is a static `Condition` instance. To help understand what the other
30+ parameters are used for, an example usage of this condition would be:
31+
32+ ~~~{.rust}
33+ do my_error::cond.trap(|raised_int| {
34+
35+ // the condition `my_error` was raised on, and the value it raised is stored
36+ // in `raised_int`. This closure must return a `~str` type (as specified in
37+ // the declaration of the condition
38+ if raised_int == 3 { ~"three" } else { ~"oh well" }
39+
40+ }).inside {
41+
42+ // The condition handler above is installed for the duration of this block.
43+ // That handler will override any previous handler, but the previous handler
44+ // is restored when this block returns (handlers nest)
45+ //
46+ // If any code from this block (or code from another block) raises on the
47+ // condition, then the above handler will be invoked (so long as there's no
48+ // other nested handler).
49+
50+ println(my_error::cond.raise(3)); // prints "three"
51+ println(my_error::cond.raise(4)); // prints "oh well"
52+
53+ }
54+ ~~~
55+
56+ Condition handling is useful in cases where propagating errors is either to
57+ cumbersome or just not necessary in the first place. It should also be noted,
58+ though, that if there is not handler installed when a condition is raised, then
59+ the task invokes `fail!()` and will terminate.
60+
61+ ## More Info
62+
63+ Condition handlers as an error strategy is well explained in the [conditions
64+ tutorial](http://static.rust-lang.org/doc/master/tutorial-conditions.html),
65+ along with comparing and contrasting it with other error handling strategies.
66+
67+ */
1468
1569use local_data;
1670use prelude:: * ;
71+ use unstable:: raw:: Closure ;
1772
18- // helper for transmutation, shown below.
19- type RustClosure = ( int , int ) ;
20-
73+ #[ doc( hidden) ]
2174pub struct Handler < T , U > {
22- handle : RustClosure ,
23- prev : Option < @Handler < T , U > > ,
75+ priv handle : Closure ,
76+ priv prev : Option < @Handler < T , U > > ,
2477}
2578
79+ /// This struct represents the state of a condition handler. It contains a key
80+ /// into TLS which holds the currently install handler, along with the name of
81+ /// the condition (useful for debugging).
82+ ///
83+ /// This struct should never be created directly, but rather only through the
84+ /// `condition!` macro provided to all libraries using libstd.
2685pub struct Condition < T , U > {
86+ /// Name of the condition handler
2787 name : & ' static str ,
88+ /// TLS key used to insert/remove values in TLS.
2889 key : local_data:: Key < @Handler < T , U > >
2990}
3091
3192impl < T , U > Condition < T , U > {
93+ /// Creates an object which binds the specified handler. This will also save
94+ /// the current handler *on creation* such that when the `Trap` is consumed,
95+ /// it knows which handler to restore.
96+ ///
97+ /// # Example
98+ ///
99+ /// ~~~{.rust}
100+ /// condition! { my_error: int -> int; }
101+ ///
102+ /// let trap = my_error::cond.trap(|error| error + 3);
103+ ///
104+ /// // use `trap`'s inside method to register the handler and then run a
105+ /// // block of code with the handler registered
106+ /// ~~~
32107 pub fn trap < ' a > ( & ' a self , h : & ' a fn ( T ) -> U ) -> Trap < ' a , T , U > {
33- unsafe {
34- let p : * RustClosure = :: cast:: transmute ( & h) ;
35- let prev = local_data:: get ( self . key , |k| k. map ( |& x| * x) ) ;
36- let h = @Handler { handle : * p, prev : prev } ;
37- Trap { cond : self , handler : h }
38- }
108+ let h: Closure = unsafe { :: cast:: transmute ( h) } ;
109+ let prev = local_data:: get ( self . key , |k| k. map ( |& x| * x) ) ;
110+ let h = @Handler { handle : h, prev : prev } ;
111+ Trap { cond : self , handler : h }
39112 }
40113
114+ /// Raises on this condition, invoking any handler if one has been
115+ /// registered, or failing the current task otherwise.
116+ ///
117+ /// While a condition handler is being run, the condition will have no
118+ /// handler listed, so a task failure will occur if the condition is
119+ /// re-raised during the handler.
120+ ///
121+ /// # Arguments
122+ ///
123+ /// * t - The argument to pass along to the condition handler.
124+ ///
125+ /// # Return value
126+ ///
127+ /// If a handler is found, its return value is returned, otherwise this
128+ /// function will not return.
41129 pub fn raise ( & self , t : T ) -> U {
42130 let msg = fmt ! ( "Unhandled condition: %s: %?" , self . name, t) ;
43131 self . raise_default ( t, || fail ! ( msg. clone( ) ) )
44132 }
45133
134+ /// Performs the same functionality as `raise`, except that when no handler
135+ /// is found the `default` argument is called instead of failing the task.
46136 pub fn raise_default ( & self , t : T , default : & fn ( ) -> U ) -> U {
47- unsafe {
48- match local_data:: pop ( self . key ) {
49- None => {
50- debug ! ( "Condition.raise: found no handler" ) ;
51- default ( )
52- }
53- Some ( handler) => {
54- debug ! ( "Condition.raise: found handler" ) ;
55- match handler. prev {
56- None => { }
57- Some ( hp) => local_data:: set ( self . key , hp)
58- }
59- let handle : & fn ( T ) -> U =
60- :: cast:: transmute ( handler. handle ) ;
61- let u = handle ( t) ;
62- local_data:: set ( self . key , handler) ;
63- u
137+ match local_data:: pop ( self . key ) {
138+ None => {
139+ debug ! ( "Condition.raise: found no handler" ) ;
140+ default ( )
141+ }
142+ Some ( handler) => {
143+ debug ! ( "Condition.raise: found handler" ) ;
144+ match handler. prev {
145+ None => { }
146+ Some ( hp) => local_data:: set ( self . key , hp)
64147 }
148+ let handle : & fn ( T ) -> U = unsafe {
149+ :: cast:: transmute ( handler. handle )
150+ } ;
151+ let u = handle ( t) ;
152+ local_data:: set ( self . key , handler) ;
153+ u
65154 }
66155 }
67156 }
68157}
69158
159+ /// A `Trap` is created when the `trap` method is invoked on a `Condition`, and
160+ /// it is used to actually bind a handler into the TLS slot reserved for this
161+ /// condition.
162+ ///
163+ /// Normally this object is not dealt with directly, but rather it's directly
164+ /// used after being returned from `trap`
70165struct Trap < ' self , T , U > {
71- cond : & ' self Condition < T , U > ,
72- handler : @Handler < T , U >
166+ priv cond : & ' self Condition < T , U > ,
167+ priv handler : @Handler < T , U >
73168}
74169
75170impl < ' self , T , U > Trap < ' self , T , U > {
171+ /// Execute a block of code with this trap handler's exception handler
172+ /// registered.
173+ ///
174+ /// # Example
175+ ///
176+ /// ~~~{.rust}
177+ /// condition! { my_error: int -> int; }
178+ ///
179+ /// let result = do my_error::cond.trap(|error| error + 3).inside {
180+ /// my_error::cond.raise(4)
181+ /// };
182+ /// assert_eq!(result, 7);
183+ /// ~~~
76184 pub fn inside < V > ( & self , inner : & ' self fn ( ) -> V ) -> V {
77185 let _g = Guard { cond : self . cond } ;
78186 debug ! ( "Trap: pushing handler to TLS" ) ;
@@ -81,8 +189,9 @@ impl<'self, T, U> Trap<'self, T, U> {
81189 }
82190}
83191
192+ #[ doc( hidden) ]
84193struct Guard < ' self , T , U > {
85- cond : & ' self Condition < T , U >
194+ priv cond : & ' self Condition < T , U >
86195}
87196
88197#[ unsafe_destructor]
0 commit comments