How can the `?` operator convert a value into a `Ok(())`?

Hi there, I'm experimenting with Rustlings, and I stumbled upon question 52, where the code looks like this:

fn main() -> Result<(), ParseIntError> {
    // [...]
    let cost = total_cost(pretend_user_input)?;
    // [...]
    Ok(())
}

In the Rust book, I see that

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 values 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.

But if I try to convert from i32 into () manually using from, I get errors:

let a: () = From::from(3 as i32);
error[E0277]: the trait bound `(): From<i32>` is not satisfied
  --> exercises/13_error_handling/errors3.rs:32:28
   |
32 |     let a: () = From::from(3 as i32);
   |                 ---------- ^^^^^^^^ the trait `From<i32>` is not implemented for `()`
   |                 |
   |                 required by a bound introduced by this call
   |
   = help: the following other types implement trait `From<T>`:
             <(T, T) as From<[T; 2]>>
             <(T, T, T) as From<[T; 3]>>
             <(T, T, T, T) as From<[T; 4]>>
             <(T, T, T, T, T) as From<[T; 5]>>
             <(T, T, T, T, T, T) as From<[T; 6]>>
             <(T, T, T, T, T, T, T) as From<[T; 7]>>
             <(T, T, T, T, T, T, T, T) as From<[T; 8]>>
             <(T, T, T, T, T, T, T, T, T) as From<[T; 9]>>
           and 4 others

For more information about this error, try `rustc --explain E0277`.
error: could not compile `exercises` (bin "errors3") due to 1 previous error

and

let a: () = <()>::from(3 as i32);
error[E0308]: mismatched types
   --> exercises/13_error_handling/errors3.rs:32:28
    |
32  |     let a: () = <()>::from(3 as i32);
    |                 ---------- ^^^^^^^^ expected `()`, found `i32`
    |                 |
    |                 arguments to this function are incorrect
    |
note: associated function defined here
   --> /home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:585:8
    |
585 |     fn from(value: T) -> Self;
    |        ^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `exercises` (bin "errors3") due to 1 previous error

So, how is a i32 converted into the unit type (), when using the ? operator? Thanks!

I don't see where you got the idea that it's i32 that got converted to (). Try operator applies From to the Err value, leaving Ok value alone.

3 Likes

You're right, I was just realising that. So the conversion only gets applied to the second type, the Error one, for the first it is simply equivalent to an unwrap(). Thank you!

2 Likes

It isn't.

The ? operator will check if its operand is a Break or a Continue kind of value [1] (with Result, these would be Err and Ok respecively).

  • If it is a Break, ? converts it with From::from and returns it from the containing function.
  • If it is a Continue, ? evaluates into the inner value, without returning from the containing function.

So, what happens in your case is that an Err(ParseIntError) will be converted to an Err(ParseIntError) using impl<T> From<T> for T and returned, while an Ok(i32) will be unwrapped to an i32 and assigned to cost. That variable is then ignored by your program, which returns Ok(()) independently from that from the tail expression.


  1. whether Try::branch returns ControlFlow::Break or ControlFlow::Continue for it ↩ī¸Ž

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.