One thing I’d like to add (thanks to @Centril for helping me formulate it on IRC!) is:
With regards to Result and Option and such, there currently exists in Rust a symmetry when looking at a function:
-> <type>
let var: <type> = <expr>
{ <expr> }
fn ... { <expr> }
|| <expr>
obj.field = <expr>
*mutable_var = <expr>
items.push(<expr>)
return <expr>
break <expr>
When dealing with a <type> of Result<Success, Failure>, the <expr> looks (or can look) like Ok(value) or Err(value), or it is something already evaluating to the <type>. The important part is that the same expression works in every case.
The Ok and Err serve as markers for “this value is the correct result” or “this value is the error”, and they work the same in every context, on any level, like any other value.
And to me, everything that breaks that symmetry (even with a function level keyword) reduces readability/grokkability of the code. As mentioned by @vorner, nested types like Result<Option<T>, E> or Option<Result<T, E>> and even more so type like Result<Result<T, E1>, E2> or Option<Option<T>> would be hit even worse by assymetry.
I should note that <expr>? does break the symmetry a bit, since you can provide it with a wider range of expressions. But putting an <expr> of the expected <type> there works generally as well, e.g. when going from Err(e1) to Err(e2).
But even the slight asymmetry here can require additional type hinting on the error type, for example when passing a closure to something, because there’s an unneeded .into() in there. That’s when I switch back to map and and_then usually.