Ergonomic error handling

Error propagation with raw Results can require tedious matching and repackaging. This tedium is largely alleviated by the try! macro, and can be completely removed (in some cases) by the "Result-impl" pattern.

The try! macro

Prefer

use std::io::{File, Open, Write, IoError};

struct Info {
    name: String,
    age: int,
    rating: int
}

fn write_info(info: &Info) -> Result<(), IoError> {
    let mut file = File::open_mode(&Path::new("my_best_friends.txt"),
                                   Open, Write);
    // Early return on error
    try!(file.write_line(format!("name: {}", info.name).as_slice()));
    try!(file.write_line(format!("age: {}", info.age).as_slice()));
    try!(file.write_line(format!("rating: {}", info.rating).as_slice()));
    return Ok(());
}

over

use std::io::{File, Open, Write, IoError};

struct Info {
    name: String,
    age: int,
    rating: int
}

fn write_info(info: &Info) -> Result<(), IoError> {
    let mut file = File::open_mode(&Path::new("my_best_friends.txt"),
                                   Open, Write);
    // Early return on error
    match file.write_line(format!("name: {}", info.name).as_slice()) {
        Ok(_) => (),
        Err(e) => return Err(e)
    }
    match file.write_line(format!("age: {}", info.age).as_slice()) {
        Ok(_) => (),
        Err(e) => return Err(e)
    }
    return file.write_line(format!("rating: {}", info.rating).as_slice());
}

See the result module documentation for more details.

The Result-impl pattern [FIXME]

[FIXME] Document the way that the io module uses trait impls on IoResult to painlessly propagate errors.