When can I do "?" operator?

I thought the following was correct

fn open_foo_file() {
    let mut file = File::create("foo.txt")?;
}

however i got the following error:

   Compiling misc v0.1.0 (/Users/Username/Developer/Rust/training/misc)
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> src/file_io.rs:53:43
    |
52  | / fn open_foo_file() {
53  | |     let mut file = File::create("foo.txt")?;
    | |                                           ^ cannot use the `?` operator in a function that returns `()`
54  | | }
    | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
note: required by `from_residual`
   --> /Users/Username/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/try_trait.rs:339:5
    |
339 |     fn from_residual(residual: R) -> Self;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


error: aborting due to previous error


For more information about this error, try `rustc --explain E0277`.

update there is a popup in vs code stating
Cargo invocation has failed: Error: exit code: 101.

You can use the "?" in any function that returns a Result or an Option.

It is somewhat easier to use in functions returning Option, because the "right-hand side" of the Option enum is always None.

Where the function returns a Result, you can only use the "?" operator if the error type produced by whatever function/code you're using the "?" operator at the end of can be converted (via the From trait) to the error type that the function you are in is also returning.

For example, if your function returns Result<AType, ErrorType> and the function you are calling and adding "?" at the end of returns Result<AnyType, AnotherErrorType>, then for it to compile the trait From<AnotherErrorType> must be implemented for ErrorType.

In your example, you are returning nothing (or more specifically the unit type ()) from your open_foo_file function. As a result there is no means for the error to be passed up the call chain to the caller of your function. I don't know exactly what error type File::create returns, but if you look at the signature it will presumably return a Result<File, SomeErrorType>. Your function must return a Result too. If you wish that to be the unit type (i.e. nothing), then it will need to return Result<(), SomeErrorType>. I suspect, however, that you might actually want to return Result<File, SomeErrorType>.

Hope that helps.

1 Like

If I understand correctly, the ? operator can return immediately with an error, meaning the function in which the ? operator is called should reflect the error in the return type.

ergo,

fn open_foo_file() -> io::Result<File> {
    let mut file = File::create("foo.txt")?;
    Ok(file)
}

Exactly correct. The extra bit of "wizardry" is that if the From trait is implemented for a particular type, then the error will be automatically converted via that trait to the right error type. So I think io:Result<File> = Result<File, std::io::Error>. Thus if, for example, File::create returned a Result<File, AnotherError>, then you could still use the "?" operator if the trait From<AnotherError> was implemented for std::io::Error. i.e impl From<AnotherError> for std::io::Error. Thus, by carefully implementing the From trait for your error types, you can keep code clean and error handling separated from business logic.

Small correction: this is not a coercion, but a conversion. The difference is that coercions are all compiler-defined, while conversions are arbitrary code.

Thanks. Updated :slight_smile:

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.