Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ParseIntError and ParseFloatError does not allow to introspect the cause #22639

Open
nagisa opened this Issue Feb 21, 2015 · 16 comments

Comments

Projects
None yet
@nagisa
Copy link
Contributor

nagisa commented Feb 21, 2015

The only way to introspect reason of .parse failure is to compare to error messages returned by description or use the following code snippet:

let overflow = "1231123123123123".parse::<u32>().err().unwrap();
let underflow = "-1231123123123123".parse::<i32>().err().unwrap();
match "999999999999".parse::<u32>() {
    Ok(n) => println!("{}", n),
    Err(ref e) if *e == overflow => println!("Overflow"),
    Err(ref e) if *e == underflow => println!("Underflow"),
    _ => panic!("wow, no introspection, very convenience")
}

@kmcallister kmcallister added the A-libs label Feb 21, 2015

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Feb 23, 2015

Note that this was purposefully done to avoid adding extra #[stable] surface for now. It should be able to expose this information through an enum in the future, for example.

@steveklabnik

This comment has been minimized.

Copy link
Member

steveklabnik commented May 24, 2016

Triage: no change here.

@rust-lang/libs is the intention still to change this to an enum of some kind in the future?

@brson brson added the T-libs label May 31, 2016

@Seeker14491

This comment has been minimized.

Copy link
Contributor

Seeker14491 commented Jan 8, 2017

Any reason to postpone this further?

@BurntSushi

This comment has been minimized.

Copy link
Member

BurntSushi commented Jan 8, 2017

@Seeker14491 I don't think it's postponed. I think someone just needs to propose an API?

@phungleson

This comment has been minimized.

Copy link
Contributor

phungleson commented Feb 7, 2017

Hey, we already have an enum IntErrorKind, just want to check if anything still need to be done to make it pub

rust/src/libcore/num/mod.rs

Lines 2769 to 2774 in fc02736

enum IntErrorKind {
Empty,
InvalidDigit,
Overflow,
Underflow,
}

@steveklabnik steveklabnik removed the A-libs label Mar 24, 2017

@gypsydave5

This comment has been minimized.

Copy link

gypsydave5 commented May 18, 2017

I just brushed up against this issue while demoing the guessing game example from the Rust Book - seemed like an easy thing to show off matching on the error kind, but turned out not so much!

@frontsideair

This comment has been minimized.

Copy link

frontsideair commented Nov 9, 2017

I also wanted to match IntErrorKind::Empty in the number game to abort on EOF, turned out to be impossible.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Nov 10, 2017

@frontsideair you can just check if the string's empty before parsing, right?

@frontsideair

This comment has been minimized.

Copy link

frontsideair commented Nov 15, 2017

I guess, but it was a fun exercise in pattern matching.

@dtolnay

This comment has been minimized.

Copy link
Member

dtolnay commented Nov 19, 2017

I see that checking for empty has come up in the context of the guessing game tutorial. Could somebody make a list of use cases from real code that needs to be able to distinguish between the following other ParseIntError cases?

  • invalid digit
  • number too large to fit in target type
  • number too small to fit in target type

In the event that empty is the only one we ever need to introspect for, I would prefer to close this and instead recommend to check == "" before parsing.

@ranma42

This comment has been minimized.

Copy link
Contributor

ranma42 commented Nov 19, 2017

It can be convenient to distinguish too large/too small numbers when accepting configuration parameters that the code will later sanitize by clamping to a valid range. Specifically, they make it possible to perform saturation of the parsed value before it is passed on.

Example: the valid range of offsets is -10..+10, therefore the application stores it in an i8. When the value it outside of the -10..+10 range the app would like to warn the user and proceed with the nearest legitimate value.

Without the ability to discriminate if the number is too big/small, it is not possible to determine what is the nearest legitimate value.

@dtolnay

This comment has been minimized.

Copy link
Member

dtolnay commented Nov 19, 2017

@ranma42 do you think that use case would be solved by rust-lang/rfcs#928?

use std::num::Saturating;

fn main() {
    let s = "-999";
    let n = match s.parse::<Saturating<i8>>()?.0 {
        std::i8::MIN ... -11 => { println!("saturating"); -10 }
        11 ... std::i8::MAX => { println!("saturating"); 10 }
        n => { println!("in range"); n }
    };
    println!("n={}", n);
}
@ranma42

This comment has been minimized.

Copy link
Contributor

ranma42 commented Nov 20, 2017

Yes, that use case would be covered if Rust had the ability to parse a Saturating<i8> (and in a convenient way, too!)

@amosbatto

This comment has been minimized.

Copy link

amosbatto commented Apr 7, 2018

The only way I found around this problem was to do something ugly like this:

match my_str.parse::<i32>() {
	Ok(x) => {my_int = x;},
	Err(e) => {
		if e.to_string() == "invalid digit found in string" {
			println!("Only positive integers are allowed!");
		}
		else if e.to_string() == "number too large to fit in target type" {
			println!("Number is too large!");
		}
		else if e.to_string() == "number too small to fit in target type" {
			println!("Number is too small!");
		}
		else if e.to_string() == "cannot parse integer from empty string" {
			println!("Number is empty!");
		}
	}
}

What is so annoying is that println!("{:?}", e) shows me ParseIntError { kind: InvalidDigit }, but kind is private so we can't access it. Either kind should be made public or a kind() function should be implemented to access to.

@Clockwork-Muse

This comment has been minimized.

Copy link

Clockwork-Muse commented May 9, 2018

I'm doing custom type parsing (something like this: X+123789Y-3453), and I'd actually like to map the errors into my own custom error type. Yes, I could implement my own integer parsing, but that would defeat the purpose of having a built-in integer parser.

My map would be something like this:

  • Empty -> MyInvalid (because the whole thing isn't empty, just one of the number portions)
  • Underflow/Overflow -> MyUnderflow/MyOverflow (although I'd be fine with just overflow, a la checked_)
  • Invalid -> MyInvalid

....except I can't do that at the moment without resorting to @amosbatto 's trick. It also makes testing very difficult, because I can't tell what error I got back (except by checking the string).

@ethanboxx

This comment has been minimized.

Copy link
Contributor

ethanboxx commented Nov 27, 2018

Hello everyone. My PR fixes this issue and has been merged.

Use the feature flag #![feature(int_error_matching)] in nightly to try it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.