Not at all. This is what the ? operator is for (was try! before).
The idiomatic rust way is to have your own Error type that implements From for other Error types that you may encounter (io::Error, Utf8Error, etc.).
Then you can simply just write let result = dosomething()?; for any operation that may fail. If an error happens, function will return with the error.
I don't think you can do it any cleaner, given the constraints Rust is operating under.
There's a couple of annoyances to it. The documentation is really sparse for something as integral as it aims to be. And there's a frustratingly large number of third-party crates that don't use standard error types and prevent you from calling chain_error to convert from their error types to yours.
But once you get the gist of it, a few lines of boilerplate gives you the ability to handle errors concisely and correctly throughout your program.
If you're designing your own errors then there's some boilerplate you need to allow for the error wrapping, though you can always take the cheap way out and use Box<Error> as your error type (as any error can be wrapped in that).
And there's an RFC that's been accepted (https://github.com/rust-lang/rfcs/blob/master/text/1937-ques...) to allow main to return a Result, so you can use the ? operator in main, which will let you avoid even more unwraps, especially in example code.
Most of the replies pointed out the new ? operator, which I'll have to check out. (One of the things that I like about the Rust community is that they constantly improve.)
The thing is, if you've only handled errors via return codes in C, then Rust's system is a major improvement. The challenge comes once you've programmed with exceptions that have inheritance. It's pretty easy to set filters higher up in the stack and "not care" lower in the stack. This allows you to effectively ignore error handling for functions that only fail in obscure corner cases, because a filter higher up in the stack can handle so many different errors. (Operations that require cleanup can also be written in a way that cleanup code always runs, as some languages support try-finally.)
That's probably the hardest thing for me to adjust to in Rust. Lifetimes take care of the try-finally part, but I still can't figure out how to have generic error filters higher in my stack.
One thing that I think does need improvement is the error handling. Far too many idiomatic examples just panic for error handling. There's no good way to just group errors by a higher-level classification and fall through to a generic error handler. As a result, the choice is between panic on error, or very detailed and verbose error handling. There's no middle ground.