Background
The Result::and_then
method can be used to chain operations that are required to all succeed:
fn compute_b(a: A, context: &Context) -> Result<B, E> { … }
fn compute_c(b: B, context: &Context) -> Result<C, E> { … }
fn compute_d(C: C, context: &Context) -> Result<D, E> { … }
fn chain(a: B, context: &Context) -> Result<D, E> {
let maybe_b = compute_b(a, context);
let maybe_c = maybe_b.and_then(|b| compute_c(b, context));
let maybe_d = maybe_c.and_then(|c| compute_d(c, context));
maybe_d
}
However, syntax makes this a bit cumbersome. The try!
macro was introduced to make this kind of thing nicer:
fn chain(a: B, context: &Context) -> Result<D, E> {
let b = try!(compute_b(a, context));
let c = try!(compute_c(b, context));
let d = try!(compute_d(c, context));
Ok(d)
}
Proposal
I’ve been using this heavily in Servo’s CSS parsing rewrite. Grammar production rules map (roughly) to functions that return Result
, concatenation maps to Result::and_then
or try!
, and alternation maps to Result::or_else
.
Still, some code would be nicer if or_else
could be replaced with a macro:
macro_rules! something_something_something {
($e: expr) => {
match $e {
Ok(value) => return Ok(value),
Err(error) => error,
}
}
}
Unresolved questions
- This new macro is very general-purpose. Would it be a good candidate for inclusion in libstd?
- What should it be named?
The name “try” applies equally to “try, and return early on error” (the current
try!
macro, equivalent toand_then
) as well as “try, and return early on success” (this new macro, equivalent toor_else
). Maybe renametry!
totry_ok!
and name the othertry_err!
?
Bonus, some representative example code
// <'border-spacing'> = <length> <length>?
fn parse_border_spacing(input: &mut Parser) -> Result<(Length, Length), ()> {
let first = try!(parse_length(input));
let second = parse_length(input).unwrap_or(first);
Ok((first, second))
}
// <'width'> = <length> | <percentage> | "auto"
fn parse_width(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
something_something_something!(parse_length(input)
.map(LengthOrPercentageOrAuto::Length));
something_something_something!(parse_percentage(input)
.map(LengthOrPercentageOrAuto::Percentage));
something_something_something!(parse_keyword(input, "auto")
.map(|()| LengthOrPercentageOrAuto::Auto));
Err(());
}