throws

Attribute Macro throws 

Source
#[throws]
Expand description

Creates a new composite error type for a function to return.

This macro can be attached to a function to declare what types of errors it can return. It accepts a list of types, delimited with | characters, for example #[throws(FooError | BarError)].

It will create a custom error enum with a variant for each listed error type. It will also change the function signature to a Result with said enum as its error type. The define_error!() macro works the same, except it isn’t attached to a function.

For example, this code:

#[throws(FooError | BarError)]
fn do_something() {
    Ok(())
}

Will translate roughly to this:

enum DoSomethingError {
    FooError(FooError),
    BarError(BarError),
}

fn do_something() -> Result<(), DoSomethingError> {
    Ok(())
}

The error enum will implement From<T> for each of its error type variants, allowing the use of the ? operator inside the function.

Additionally, it is possible to add “suberror” types prefixed with the break keyword. It must be used with a error type that implements the SubError trait, which all error types generated by this crate do. Doing so will generate a From<T> implementation that upcasts the suberror into the error type returned by the function. (Hence “breaking” its variants apart.) To do this, it’s variants have to be a subset of the errors the function can return.

Suppose you have two functions:

#[throws(FooError | BarError)]
fn first() {
    unimplemented!("Some complicated implementation...")
}

#[throws(FooError | BarError | BazError | break FirstError)]
fn second() {
    first()?;
    Ok(())
}

If the first() function returns FirstError::FooError, the second() function will return SecondError::FooError. Similarly, FirstError::BarError will map to SecondError::BarError. SecondError::BazError can never be returned.

The enum will also have a Display implementation that simply forwards to the Display implementation of each variant. It will also implement Error and provide the Error::source() method.

§Syntax

The main body of the macro arguments consists of a list of types, separated by | characters. They can either be variants, which are written as just a type name, or suberrors, which are prefixed with the break keyword. For example, #[throws(FooError | break BarError)] declares a variant FooError and a suberror BarError.

The name of a variant can be generated automatically if the type is a simple path. The generated name will consist of the entire path converted to camel case. For example, io:Error will become IoError and ParseIntError will remain ParseIntError. The name can be specified explicitly with the as keyword, for example io::Error as InputError.

The name of the error type can be generated automatically as well. It will be generated by converting the function name to camel case and appending “Error”. For example, fn upload_image() will return a UploadImageError. The name can also be specified explicitly using type SomeError = .... For example, to name your error type SubmitError, you should use #[throws(type SubmitError = FooError | BarError)].

§Examples

#[throws(ParseIntError | io::Error)]
fn main() {
    let mut sum = 0u64;

    for line in stdin().lock().lines() {
        let value: u64 = line?.parse()?;
        sum += value;
    }

    writeln!(stdout(), "{sum}")?;
    Ok(())
}
#[throws(type ParseListError = ParseIntError as ParseElementError)]
fn parse(s: &str) -> Vec<u64> {
    let mut result = Vec::new();

    for line in s.split('\n') {
        result.push(line.parse()?);
    }

    Ok(result)
}

#[throws(io::Error | ParseIntError | break ParseListError)]
pub fn load_int_list(path: &Path) -> Vec<u64> {
    let content = fs::read_to_string(path)?;
    Ok(parse(&content)?)
}