8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- /*! Condition handling */
11
+ /*!
12
12
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
+ */
14
68
15
69
use local_data;
16
70
use prelude:: * ;
71
+ use unstable:: raw:: Closure ;
17
72
18
- // helper for transmutation, shown below.
19
- type RustClosure = ( int , int ) ;
20
-
73
+ #[ doc( hidden) ]
21
74
pub struct Handler < T , U > {
22
- handle : RustClosure ,
23
- prev : Option < @Handler < T , U > > ,
75
+ priv handle : Closure ,
76
+ priv prev : Option < @Handler < T , U > > ,
24
77
}
25
78
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.
26
85
pub struct Condition < T , U > {
86
+ /// Name of the condition handler
27
87
name : & ' static str ,
88
+ /// TLS key used to insert/remove values in TLS.
28
89
key : local_data:: Key < @Handler < T , U > >
29
90
}
30
91
31
92
impl < 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
+ /// ~~~
32
107
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 }
39
112
}
40
113
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.
41
129
pub fn raise ( & self , t : T ) -> U {
42
130
let msg = fmt ! ( "Unhandled condition: %s: %?" , self . name, t) ;
43
131
self . raise_default ( t, || fail ! ( msg. clone( ) ) )
44
132
}
45
133
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.
46
136
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)
64
147
}
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
65
154
}
66
155
}
67
156
}
68
157
}
69
158
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`
70
165
struct 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 >
73
168
}
74
169
75
170
impl < ' 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
+ /// ~~~
76
184
pub fn inside < V > ( & self , inner : & ' self fn ( ) -> V ) -> V {
77
185
let _g = Guard { cond : self . cond } ;
78
186
debug ! ( "Trap: pushing handler to TLS" ) ;
@@ -81,8 +189,9 @@ impl<'self, T, U> Trap<'self, T, U> {
81
189
}
82
190
}
83
191
192
+ #[ doc( hidden) ]
84
193
struct Guard < ' self , T , U > {
85
- cond : & ' self Condition < T , U >
194
+ priv cond : & ' self Condition < T , U >
86
195
}
87
196
88
197
#[ unsafe_destructor]
0 commit comments