# Using Result Combinator Functions in Rust

flow by checking for errors in a succinct, elegant way

Using Rust for the first time, error handling was my biggest stumbling block. Was this value a Result<T, E> or just a T? And the right T? The right E? I couldn’t just write the code I wanted to write. It felt confusing and overly elaborate.

But after a while, I started to get a feel for the basics of using Result. I discovered that the combinator methods Result provides, like map, or_else and ok, made error handling fun. Well, maybe that's a bit of an overstatement. They made using Result a bit easier, at least.

So far my favorite Result combinator method is and_then. Using and_then is actually fun! For example, I wrote this Rust code to generate the static HTML pages for this blog site:

```let count = all_posts.len();
all_posts.sort_by_key(|p| Reverse(p.date));
let params = CompileParams {all_posts: all_posts, output_path: output_path, draft: draft};
Ok(params).and_then(compile_posts)
.and_then(compile_home_page)
.map(|_output| count)```

Ignoring the details about sorting and counting, my code:

• First creates a struct holding input parameters, and wraps it using Ok(params)
• And then tries to compile all the posts in my blog, passing in the input parameters
• And then if this was successful, it tries to compile the home page (index.html)
• And then if this was successful, it tries to compile the RSS feed (index.xml)

If there was an error at any time in this process, it short circuits and stops. Here’s a flowchart that illustrates this control flow:

The happy path is from top to bottom, along the left side. If any of the compile methods fail, and_then short circuits the happy path and jumps to the end.

## Matching Result Types

To chain and_then methods together like this, I used the same input and output types for each of the compile methods:

`fn compile_posts(params: CompileParams) -> Result<CompileParams, InvalidPostError>`
`fn compile_home_page(params: CompileParams) -> Result<CompileParams, InvalidPostError>`
`fn compile_rss_feed(params: CompileParams) -> Result<CompileParams, InvalidPostError>`

Each method expects a CompileParams struct, and returns one wrapped in Result. Rust unwraps the CompileParams from one call and passes it to the next.

I use InvalidPostError throughout my code to provide a consistent way to return errors. This was a bit of a challenge at first, until I realized it was easy to cast other types of errors into InvalidPostError like this:

```impl From<std::io::Error> for InvalidPostError {
fn from(e: std::io::Error) -> InvalidPostError {
let message = format!("{}", e);
InvalidPostError::new(&message)
}
}```

Now the Rust compiler knows how to map a std::io::Error into an InvalidPostError.

## Error Handling the Old Fashioned Way

Here’s the code I didn’t have to write: (This is Ruby; substitute your favorite PL that doesn't support monadic error handling.)

```if compile_posts(params)
if compile_home_page(params)
puts "Success!"
else
end
else
end
else
puts "Error compiling a blog post"
end```

I didn’t have to write a series of if/else blocks. This would have been tedious to write and tedious to read. And I probably would have forgotten (or have been too lazy) to check one of the return values.

And I didn’t have to write this code either:

```def compile_posts(params)
raise InvalidPostError.new("Failed compiling the posts")
end

def compile_home_page(params)
end

raise InvalidPostError.new("Failed compiling the RSS feed")
end

begin
compile_posts(params)
compile_home_page(params)
puts "Success"
rescue InvalidPostError => e
puts e.message
end```

Once again this is fragile: I might raise the wrong exception type or not raise one at all. Or I might rescue the wrong type. Worse, there’s no indication at the call site what might happen.

To be honest, I probably won’t bother handling errors at all for a simple Ruby script like this. If an exception happens someday while building my blog site, then I’ll deal with it then. I’d probably just write this code:

```compile_posts(params)
compile_home_page(params)
```Ok(params).and_then(compile_posts)