#syntax-error #error #early #syntax #return #try

no-std tear

Typed early returns and loop control + Syntax sugar for try!-like error handling

7 releases (4 breaking)

0.5.1 Apr 11, 2021
0.5.0 Apr 11, 2021
0.4.0 Aug 3, 2020
0.3.0 May 27, 2020
0.1.1 May 19, 2020

#737 in Rust patterns


Used in switchable

MIT/Apache

66KB
533 lines

tear

Typed early returns and loop control + Syntax sugar for try!-like error handling

Works with Rust v1.34+ (released on 11 April 2019)

Synopsis

Import the macros into your module:

use tear::prelude::*;

Explicit error-handling syntax with terror!:

let handled = terror! { can_error() => print_error };
let variant = terror! { can_io_error() => CustomError::Io };

// Equivalent using `?`:
let handled = can_error().map_err(print_error)?;
let variant = can_io_error.map_err(CustomError::Io)?;

Early loop continue with twist!:

for re in regexes_strings {
    // Skip iteration if the regex fails to compile
    let re = twist! { Regex::new(re) => |_| next!() }

    // Use regex...

Keyword-like early returns with tear_if!:

fn divide_i32 (num: i32, denom: i32) -> Option<f32> {
    // Return early if dividing by 0
    tear_if! { denom == 0, None };

    // Compute quotient...

Typed early returns with tear!:

// Tells the calling function to return early on failure
fn get_value_or_return() -> ValRet<String, i32> { Ret(-1) }

fn status_code() -> i32 {
    let v = tear! { get_value_or_return() };

    // Process value...

See the documentation for more info.

Changelog

See CHANGELOG.

Rationale

I wanted to make early returns more explicit.

Normally, you need to read until the end of the if body to know if it returns early or not. tear_if places that information at the beginning of the block.

I wanted typed early returns because it is useful for passing exitcodes up the callchain.

Having a typed early return allows you to have functions that can force their caller to return early. It's an action at a distance inspired by how Slips work in Raku.

I wanted annotated failure points instead of too many combinators.

The ? operator works is essentially a conditional early-return. To convert the errors you get to the right type, you need to use combinators. I find it hard to discern that those combinators are meant for error handling.

Something like this:

let path = find_config_file().ok_or(Error::FindPathF)?
let mut file = get_file_buffer(&path).map_err(Error::GetFileF)?;

The terror! macro makes the error handling more explicit:

let path = terror! { find_config_file() => Error::FindPathF };
let mut file = terror! { get_file_buffer(&path) => Error::GetFileF };

Loop control and early returns are similar

I already implemented typed early return, so why not implement typed loop controls well ? They're the same kind of useful.

See also

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies