Correct use of !try

I’m learning how to use !try but I having some difficulties to put it to work, need to understand how to return correctly, in following example I want to use try! and return a Result(String).

fn create_transport() -> Result<String, std::io::Error> {
    let network = if cfg!(debug_assertions) {
        "ws://localhost:7545"
    } else {
        return Err(std::io::Error::new(std::io::ErrorKind::Other, "Ops error"))
    };

    Ok(network.to_string())
}

pub fn run() {
    let transport = try!(create_transport());
}

However it’s given a compile error

Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:12:21
   |
12 |     let transport = try!(create_transport());
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
   |
   = note: expected type `()`
              found type `std::result::Result<_, _>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to previous error

Are you trying to return a Result from fn run?
If so,here’s how you can write run:

pub fn run() -> Result<(), std::io::Error>{
    let transport = try!(create_transport());
    // do stuff with transport
    Ok(())
}

I am assuming that because the function is called run that it does not return the String.

By the way,the more modern way to return a Result (on Err(_)) is using the ? operator,so the let transport line would be written like this:

let transport = create_transport()?;
3 Likes

Ok got it ! so the use of !try or ? on the current function forces the function to return Result

Kind of, it has to match what you are using the ? operator on.

If you do

fn foo(a: Option<u32>) -> ???? {
    println!("{}", a?);
    unimplemented!()
}

then ???? must be an Option<_>. If you do

fn bar(a: Result<u32, Err>) -> ???? {
    println!("{}", a?);
    unimplemented!()
}

then ???? must be a Result<_, _>.

I’m a bit confused, how this code is supposed to work? It’s clear that input and output should match, but what are println! and unimplemented! here used for?

To put it another way - the following two code snippets are functionally identical*:


let x = this_might_fail()?;
let x = match this_might_fail() {
    Ok(inner) => inner,
    Err(err) => return Err(err),
};

Similarly, for Option:

let x = this_might_be_empty()?;
let x = match this_might_be_empty() {
    Some(inner) => inner,
    None => return None,
};

This is why you have to have a specific function signature to use ? - you’re effectively adding an early return into your function, and the type being returned must match up.

* This is slightly oversimplifying, as ? can also do some conversions via the From trait.

5 Likes

I hope you read the book and know what println! is for. unimplemented! just informs the compiler, that the following code is unimplemented and so rust does not complain that all paths do return a value (I had to write Some(3) respectivly Ok(3) to return something from that functions, which would be too verbose).

1 Like

I see. Just didn’t understand that here the “return early” will mean “return before the println! executes”. Maybe in this way it should be a bit more clear:

fn print_if_some(a: Option<u32>) -> Option<u32> {
    println!("{}", a?);
    a
}

fn print_if_ok(a: Result<u32, Err>) -> Result<u32, Err> {
    println!("{}", a?);
    a
}