[Resolved] Best way to short-circuit upon Err(_)


#1

I can’t seem to find an elegant way to short-circuit my function when I see an Err.

This way gives me a warning that time_float’s initial value is never read.

    let mut time_float = 0.0;
    if let Ok::<f64, std::num::ParseFloatError>(parsed) = duration_str.trim().parse() {
        time_float = parsed; // warning: value assigned to `time_float` is never read
    } else {
        return Err("Could not parse to float.");
    }

This way requires an unreachable return value after the return.

    let mut time_float : f64 = match duration_str.trim().parse() {
        Ok(x) => x,
        Err(_) => {
            return Err("Could not parse to float.");
            0.0 // warning: unreachable expression
        }
    };

There has got to be a better way!


#2

Since the compiler is smart enough to see that these 0.0 expressions are never used, I wonder what would happen if you removed them…?

nudge, nudge, wink, wink


And because I like to tease, here’s another closely related example. Yes, it compiles, and yes, it’s sound:

enum ThisTypeIsImpossibleToCreate { }

fn foo() -> ThisTypeIsImpossibleToCreate {
    panic!("yet this function claims to return one");
}

#3

If your function returns an error type that is convertible from ParseFloatError (using error-chain, probably) then this is all you need:

let time_float = duration_str.trim().parse()?;

Here is a complete example to get you started with the library but check out their extensive documentation.

#[macro_use]
extern crate error_chain;

error_chain! {
    foreign_links {
        ParseFloat(::std::num::ParseFloatError);
    }
}

fn print_time(t: f64) {
    println!("time: {}", t);
}

fn clean_cut(input: &str) -> Result<()> {
    let t = input.trim().parse()?;
    print_time(t);
    Ok(())
}

#4

the best way i know of

let time_float = duration_str.trim().parse::<f64>().or(Err("Could not parse to float."))?;

#5

I thought it went without saying that the compiler wouldn’t allow it, so I didn’t say it!

58 |         time_float = result
   |                      ^^^^^^ expected floating-point variable, found enum `std::result::Result`
   |
   = note: expected type `{float}`
   = note:    found type `std::result::Result<_, _>`

#6

Then clearly your example is different from your actual code.

let time_float;
if let Ok::<f64, std::num::ParseFloatError>(parsed) = duration_str.trim().parse() {
    time_float = parsed;
} else {
    return Err("Could not parse to float.");
}
let time_float : f64 = match duration_str.trim().parse() {
    Ok(x) => x,
    Err(_) => {
        return Err("Could not parse to float.");
    }
};

#7

@juggle-tux

That works! So that’s where you put the turbofish.

I’m not familiar with the ? suffix. What is it called? (Or where can I read about it?) I am mystified…


#8

I’m sorry, I don’t follow. I’ve been copy-and-pasting from my code. You’re welcome to take a look. It’s this file in this commit.

Ah, now I see. I had had some other experimental code uncommented when I tried uncommenting the 0.0 line, and I pasted an unrelated error in without realizing it. You obviously caught it, though.

This works without error, though I could have sworn that it didn’t when I wrote it initially, and later when I tried the same thing in other code:

    let mut time_float : f64 = match duration_str.trim().parse() {
        Ok(x) => x,
        Err(_) => {
            return Err("Could not parse to float.");
            //0.0
        }
    };

#9

@dtolnay Thanks for your error-chain suggestion. At the moment, I’m trying to stick to the standard library as much as possible so I can thoroughly learn any needed boilerplate. It’s good to know about error-chain for the future, though!


#10

Ah, I found an explanation of the ? operator in the 1.13 release announcement. Haven’t found the official docs, yet.


#11

Okay, so the two best options I see are:

(With @ExpHP’s nudging)

    let mut time_float : f64 = match duration_str.trim().parse() {
        Ok(x) => x,
        Err(_) => {
            return Err("Could not parse to float.");
        }
    };

and from @juggle-tux:

let mut time_float = duration_str.trim().parse::<f64>().or(Err("Could not parse to float."))?;

I also gather from @dtolnay’s information that if I implement the std::num::ParseFloatError trait either using a helper crate, or just manually, then I could even do this. Though I’m unclear as to whether I would be able to customize my Err in this case.

let mut time_float = duration_str.trim().parse::<f64>()?;
// or, I think equivalently
let mut time_float : f64 = duration_str.trim().parse()?;

#12

You may have more luck looking up information about the try! macro, which was around in rust a long time before ? supplanted it.

To my understanding, x? right now behaves pretty much exactly as try!(x) used to.

(Technically speaking, ? is supposed to be more general than try!() thanks to the new Carrier trait, whereas try! was strictly for Result. However, Result is currently the only type that implements Carrier.)