This operator will not only return the error if there is one, but will also convert it to the required type. It converts it with a call to Into::into(), Into being a trait. But why did we implement the From trait, instead of Into? Because there's a generic implementation of Into which is based on From:
impl<T, U> Into<U> for T where U: From<T>,
Thanks to this implementation, we rarely need to implement the Into trait ourselves. We only need to implement the From trait.
This means that we can rewrite the previous code as follows:
let val = result?;
And it will behave exactly the same as before.