1

Say I have the following:

use std::fs::File; impl From<i32> for Blah { fn from(b:i32) -> Blah { Blah {} } } fn main() {} enum MyError { ParseError, } impl From<std::io::Error> for MyError { fn from(_:std::io::Error) -> Self { MyError::ParseError } } fn get_result() -> Result<Blah, MyError> { let mut file = File::create("foo.txt")?; } 

This compiles fine. I don't understand how.

File::create throws an std::io::error, which we're trying to wrap in a MyError. But we never explicitly call from anywhere!? How does it compile?

As the comments from this answer Rust understanding From trait indicate, you do have to explicitly call from.

So, how is the above snippet compiling?

0

2 Answers 2

4

The difference is stated in The Rust Programming Language, chapter 9, section 2, when talking about the ? operator:

Error values that have the ? operator called on them go through the from function, defined in the From trait in the standard library, which is used to convert errors from one type into another. When the ? operator calls the from function, the error type received is converted into the error type defined in the return type of the current function. This is useful when a function returns one error type to represent all the ways a function might fail, even if parts might fail for many different reasons. As long as each error type implements the from function to define how to convert itself to the returned error type, the ? operator takes care of the conversion automatically.

You have provided this implementation of From<std::io::Error> for that error type, therefore the code will work and convert values of this type automatically.

Sign up to request clarification or add additional context in comments.

Comments

4

The magic is in the ? operator.

let mut file = File::create("foo.txt")?; 

expands to something like (source)

let mut file = match File::create("foo.txt") { Ok(t) => t, Err(e) => return Err(e.into()), }; 

This uses the Into trait, which is the counterpart to the From trait: e.into() is equivalent to T::from(e). Here you have the explicit conversion.

(There is an automatic impl<T, U> Into<U> for T for every impl<T, U> From<T> for U, which is why implementing From is enough.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.