Given the following two error types and functions to illustrate their usage (Rust Playground link):
#[derive(std::fmt::Debug)] struct MyError; #[derive(std::fmt::Debug)] struct OtherError; impl std::error::Error for MyError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } impl std::fmt::Display for MyError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "MyError") } } impl std::fmt::Display for OtherError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "OtherError") } } impl std::error::Error for OtherError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } impl From<OtherError> for MyError { fn from(_: OtherError) -> Self { MyError {} } } fn my_error() -> Result<(), MyError> { Ok(()) } fn other_error() -> Result<(), OtherError> { Ok(()) } If I am in a function that returns Result with MyError as its Error type, I can call both functions returning MyError and OtherError because there's a From converting between them.
However, I cannot simply return the Result for the "other" type, I need to use ? followed by Ok(()) instead. This looks inconsistent to me.
For example, this works fine:
fn main() -> Result<(), MyError> { my_error() } This also does:
fn main() -> Result<(), MyError> { my_error()?; other_error()?; my_error() } But this fails:
fn main() -> Result<(), MyError> { my_error()?; other_error() } Error:
error[E0308]: mismatched types --> src/main.rs:43:5 | 41 | fn main() -> Result<(), MyError> { | ------------------- expected `std::result::Result<(), MyError>` because of return type 42 | my_error()?; 43 | other_error() | ^^^^^^^^^^^^^ expected struct `MyError`, found struct `OtherError` | = note: expected enum `std::result::Result<_, MyError>` found enum `std::result::Result<_, OtherError>` Why is that?
This makes some of my code more verbose, as I found out I need to do this to get it to work:
fn main() -> Result<(), MyError> { my_error()?; other_error()?; Ok(()) } Is this the only solution? I am more interested in understanding the reason it works this way, but if I am doing something silly feel free to point out what could be done better.
?is sugar, which includes converting the error usingFrom. If just doingreturning alone did that, then that would be kinda scary.into()or whatever to invoke theFromthough? Does it say it does that somewhere??shouldn't work the way it does in my example.From:from(err)and this other answer you link to doesn't either. It would be nice to have a "proper" answer showing whattry!actually does (not shown in that answer). Only the RFC mentioned in that answer seems to show usage ofe.into()but that was a design proposal (which looks different from current Rust). Very unsatisfactory situation IMO.?operator "replaces" it...