result.rs 9.8 KB
Newer Older
1
//! A type representing either success or failure
2

3
import either::Either;
4

5
/// The result type
6
enum result<T, U> {
7
    /// Contains the successful result value
8
    ok(T),
9
    /// Contains the error value
10
    err(U)
11 12
}

13 14 15 16 17 18 19
/**
 * Get the value out of a successful result
 *
 * # Failure
 *
 * If the result is an error
 */
B
Brian Anderson 已提交
20
pure fn get<T: copy, U>(res: result<T, U>) -> T {
21
    match res {
B
Brian Anderson 已提交
22 23
      ok(t) => t,
      err(the_err) => unchecked {
P
Paul Stansifer 已提交
24
        fail fmt!("get called on error result: %?", the_err)
25 26 27 28
      }
    }
}

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/**
 * Get a reference to the value out of a successful result
 *
 * # Failure
 *
 * If the result is an error
 */
pure fn get_ref<T, U>(res: &a/result<T, U>) -> &a/T {
    match *res {
        ok(ref t) => t,
        err(ref the_err) => unchecked {
            fail fmt!("get_ref called on error result: %?", the_err)
        }
    }
}

45 46 47 48 49 50 51
/**
 * Get the value out of an error result
 *
 * # Failure
 *
 * If the result is not an error
 */
B
Brian Anderson 已提交
52
pure fn get_err<T, U: copy>(res: result<T, U>) -> U {
53
    match res {
B
Brian Anderson 已提交
54 55
      err(u) => u,
      ok(_) => fail ~"get_error called on ok result"
56 57 58
    }
}

59
/// Returns true if the result is `ok`
60
pure fn is_ok<T, U>(res: result<T, U>) -> bool {
61
    match res {
B
Brian Anderson 已提交
62 63
      ok(_) => true,
      err(_) => false
64 65 66
    }
}

67
/// Returns true if the result is `err`
68 69
pure fn is_err<T, U>(res: result<T, U>) -> bool {
    !is_ok(res)
70 71
}

72 73 74 75 76 77
/**
 * Convert to the `either` type
 *
 * `ok` result variants are converted to `either::right` variants, `err`
 * result variants are converted to `either::left`.
 */
78
pure fn to_either<T: copy, U: copy>(res: result<U, T>) -> Either<T, U> {
79
    match res {
80 81
      ok(res) => either::Right(res),
      err(fail_) => either::Left(fail_)
82 83 84
    }
}

85 86 87 88 89 90 91 92 93 94 95 96 97 98
/**
 * Call a function based on a previous result
 *
 * If `res` is `ok` then the value is extracted and passed to `op` whereupon
 * `op`s result is returned. if `res` is `err` then it is immediately
 * returned. This function can be used to compose the results of two
 * functions.
 *
 * Example:
 *
 *     let res = chain(read_file(file)) { |buf|
 *         ok(parse_buf(buf))
 *     }
 */
99 100
fn chain<T, U: copy, V: copy>(res: result<T, V>, op: fn(T) -> result<U, V>)
    -> result<U, V> {
101
    match res {
B
Brian Anderson 已提交
102 103
      ok(t) => op(t),
      err(e) => err(e)
104 105
    }
}
106

107 108 109 110 111 112 113 114
/**
 * Call a function based on a previous result
 *
 * If `res` is `err` then the value is extracted and passed to `op`
 * whereupon `op`s result is returned. if `res` is `ok` then it is
 * immediately returned.  This function can be used to pass through a
 * successful result while handling an error.
 */
115 116 117 118
fn chain_err<T: copy, U: copy, V: copy>(
    res: result<T, V>,
    op: fn(V) -> result<T, U>)
    -> result<T, U> {
119
    match res {
B
Brian Anderson 已提交
120 121
      ok(t) => ok(t),
      err(v) => op(v)
122 123 124
    }
}

125 126 127 128 129 130 131 132 133 134 135 136 137 138
/**
 * Call a function based on a previous result
 *
 * If `res` is `ok` then the value is extracted and passed to `op` whereupon
 * `op`s result is returned. if `res` is `err` then it is immediately
 * returned. This function can be used to compose the results of two
 * functions.
 *
 * Example:
 *
 *     iter(read_file(file)) { |buf|
 *         print_buf(buf)
 *     }
 */
139
fn iter<T, E>(res: result<T, E>, f: fn(T)) {
140
    match res {
B
Brian Anderson 已提交
141 142
      ok(t) => f(t),
      err(_) => ()
143 144 145
    }
}

146 147 148 149 150 151 152 153
/**
 * Call a function based on a previous result
 *
 * If `res` is `err` then the value is extracted and passed to `op` whereupon
 * `op`s result is returned. if `res` is `ok` then it is immediately returned.
 * This function can be used to pass through a successful result while
 * handling an error.
 */
154
fn iter_err<T, E>(res: result<T, E>, f: fn(E)) {
155
    match res {
B
Brian Anderson 已提交
156 157
      ok(_) => (),
      err(e) => f(e)
158 159 160
    }
}

161 162 163 164 165 166 167 168 169 170 171 172 173 174
/**
 * Call a function based on a previous result
 *
 * If `res` is `ok` then the value is extracted and passed to `op` whereupon
 * `op`s result is wrapped in `ok` and returned. if `res` is `err` then it is
 * immediately returned.  This function can be used to compose the results of
 * two functions.
 *
 * Example:
 *
 *     let res = map(read_file(file)) { |buf|
 *         parse_buf(buf)
 *     }
 */
175 176
fn map<T, E: copy, U: copy>(res: result<T, E>, op: fn(T) -> U)
  -> result<U, E> {
177
    match res {
B
Brian Anderson 已提交
178 179
      ok(t) => ok(op(t)),
      err(e) => err(e)
180 181 182
    }
}

183 184 185 186 187 188 189 190
/**
 * Call a function based on a previous result
 *
 * If `res` is `err` then the value is extracted and passed to `op` whereupon
 * `op`s result is wrapped in an `err` and returned. if `res` is `ok` then it
 * is immediately returned.  This function can be used to pass through a
 * successful result while handling an error.
 */
191 192
fn map_err<T: copy, E, F: copy>(res: result<T, E>, op: fn(E) -> F)
  -> result<T, F> {
193
    match res {
B
Brian Anderson 已提交
194 195
      ok(t) => ok(t),
      err(e) => err(op(e))
196 197 198
    }
}

B
Brian Anderson 已提交
199
impl<T, E> result<T, E> {
200
    fn is_ok() -> bool { is_ok(self) }
201

202
    fn is_err() -> bool { is_err(self) }
203

204
    fn iter(f: fn(T)) {
205
        match self {
B
Brian Anderson 已提交
206 207
          ok(t) => f(t),
          err(_) => ()
208 209 210 211
        }
    }

    fn iter_err(f: fn(E)) {
212
        match self {
B
Brian Anderson 已提交
213 214
          ok(_) => (),
          err(e) => f(e)
215 216
        }
    }
217 218
}

B
Brian Anderson 已提交
219
impl<T: copy, E> result<T, E> {
220 221 222
    fn get() -> T { get(self) }

    fn map_err<F:copy>(op: fn(E) -> F) -> result<T,F> {
223
        match self {
B
Brian Anderson 已提交
224 225
          ok(t) => ok(t),
          err(e) => err(op(e))
226 227 228 229
        }
    }
}

B
Brian Anderson 已提交
230
impl<T, E: copy> result<T, E> {
231
    fn get_err() -> E { get_err(self) }
232 233

    fn map<U:copy>(op: fn(T) -> U) -> result<U,E> {
234
        match self {
B
Brian Anderson 已提交
235 236
          ok(t) => ok(op(t)),
          err(e) => err(e)
237 238
        }
    }
239
}
240

B
Brian Anderson 已提交
241
impl<T: copy, E: copy> result<T, E> {
242 243 244 245 246 247
    fn chain<U:copy>(op: fn(T) -> result<U,E>) -> result<U,E> {
        chain(self, op)
    }

    fn chain_err<F:copy>(op: fn(E) -> result<T,F>) -> result<T,F> {
        chain_err(self, op)
248
    }
249
}
250

251 252 253 254 255 256 257 258 259 260
/**
 * Maps each element in the vector `ts` using the operation `op`.  Should an
 * error occur, no further mappings are performed and the error is returned.
 * Should no error occur, a vector containing the result of each map is
 * returned.
 *
 * Here is an example which increments every integer in a vector,
 * checking for overflow:
 *
 *     fn inc_conditionally(x: uint) -> result<uint,str> {
B
Brian Anderson 已提交
261 262
 *         if x == uint::max_value { return err("overflow"); }
 *         else { return ok(x+1u); }
263
 *     }
264 265
 *     map(~[1u, 2u, 3u], inc_conditionally).chain {|incd|
 *         assert incd == ~[2u, 3u, 4u];
266 267
 *     }
 */
268
fn map_vec<T,U:copy,V:copy>(
269
    ts: &[T], op: fn(T) -> result<V,U>) -> result<~[V],U> {
270

271
    let mut vs: ~[V] = ~[];
272
    vec::reserve(vs, vec::len(ts));
B
Brian Anderson 已提交
273
    for vec::each(ts) |t| {
274
        match op(t) {
B
Brian Anderson 已提交
275 276
          ok(v) => vec::push(vs, v),
          err(u) => return err(u)
277 278
        }
    }
B
Brian Anderson 已提交
279
    return ok(vs);
280 281
}

282
fn map_opt<T,U:copy,V:copy>(
B
Brian Anderson 已提交
283
    o_t: Option<T>, op: fn(T) -> result<V,U>) -> result<Option<V>,U> {
284

285
    match o_t {
B
Brian Anderson 已提交
286 287 288
      None => ok(None),
      Some(t) => match op(t) {
        ok(v) => ok(Some(v)),
B
Brian Anderson 已提交
289
        err(e) => err(e)
290 291 292 293
      }
    }
}

294 295 296 297 298 299 300 301 302
/**
 * Same as map, but it operates over two parallel vectors.
 *
 * A precondition is used here to ensure that the vectors are the same
 * length.  While we do not often use preconditions in the standard
 * library, a precondition is used here because result::t is generally
 * used in 'careful' code contexts where it is both appropriate and easy
 * to accommodate an error like the vectors being of different lengths.
 */
303
fn map_vec2<S,T,U:copy,V:copy>(ss: &[S], ts: &[T],
304
                               op: fn(S,T) -> result<V,U>) -> result<~[V],U> {
305

306
    assert vec::same_length(ss, ts);
307
    let n = vec::len(ts);
308
    let mut vs = ~[];
309 310 311
    vec::reserve(vs, n);
    let mut i = 0u;
    while i < n {
312
        match op(ss[i],ts[i]) {
B
Brian Anderson 已提交
313 314
          ok(v) => vec::push(vs, v),
          err(u) => return err(u)
315 316 317
        }
        i += 1u;
    }
B
Brian Anderson 已提交
318
    return ok(vs);
319 320
}

321 322 323 324 325
/**
 * Applies op to the pairwise elements from `ss` and `ts`, aborting on
 * error.  This could be implemented using `map2()` but it is more efficient
 * on its own as no result vector is built.
 */
326
fn iter_vec2<S,T,U:copy>(ss: &[S], ts: &[T],
327
                         op: fn(S,T) -> result<(),U>) -> result<(),U> {
328

329
    assert vec::same_length(ss, ts);
330 331 332
    let n = vec::len(ts);
    let mut i = 0u;
    while i < n {
333
        match op(ss[i],ts[i]) {
B
Brian Anderson 已提交
334 335
          ok(()) => (),
          err(u) => return err(u)
336 337 338
        }
        i += 1u;
    }
B
Brian Anderson 已提交
339
    return ok(());
340 341
}

342
/// Unwraps a result, assuming it is an `ok(T)`
343 344
fn unwrap<T, U>(-res: result<T, U>) -> T {
    unsafe {
345
        let addr = match res {
B
Brian Anderson 已提交
346 347
          ok(x) => ptr::addr_of(x),
          err(_) => fail ~"error result"
348 349 350
        };
        let liberated_value = unsafe::reinterpret_cast(*addr);
        unsafe::forget(res);
B
Brian Anderson 已提交
351
        return liberated_value;
352
    }
353 354
}

355 356
#[cfg(test)]
mod tests {
357
    fn op1() -> result::result<int, ~str> { result::ok(666) }
358

359
    fn op2(&&i: int) -> result::result<uint, ~str> {
360 361
        result::ok(i as uint + 1u)
    }
362

363
    fn op3() -> result::result<int, ~str> { result::err(~"sadface") }
364 365 366 367 368 369 370 371

    #[test]
    fn chain_success() {
        assert get(chain(op1(), op2)) == 667u;
    }

    #[test]
    fn chain_failure() {
372
        assert get_err(chain(op3(), op2)) == ~"sadface";
373
    }
374 375 376 377

    #[test]
    fn test_impl_iter() {
        let mut valid = false;
378
        ok::<~str, ~str>(~"a").iter(|_x| valid = true);
379 380
        assert valid;

381
        err::<~str, ~str>(~"b").iter(|_x| valid = false);
382 383 384 385 386 387
        assert valid;
    }

    #[test]
    fn test_impl_iter_err() {
        let mut valid = true;
388
        ok::<~str, ~str>(~"a").iter_err(|_x| valid = false);
389 390 391
        assert valid;

        valid = false;
392
        err::<~str, ~str>(~"b").iter_err(|_x| valid = true);
393 394 395 396 397
        assert valid;
    }

    #[test]
    fn test_impl_map() {
398 399
        assert ok::<~str, ~str>(~"a").map(|_x| ~"b") == ok(~"b");
        assert err::<~str, ~str>(~"a").map(|_x| ~"b") == err(~"a");
400 401 402 403
    }

    #[test]
    fn test_impl_map_err() {
404 405
        assert ok::<~str, ~str>(~"a").map_err(|_x| ~"b") == ok(~"a");
        assert err::<~str, ~str>(~"a").map_err(|_x| ~"b") == err(~"b");
406
    }
407
}