Basic problem parsing a String to int

Okay, I've beat my head against a wall long enough, before I pass out it's time to call the pros from Dover. :slight_smile:

I'm doing something that should be trivial, parsing a String into an integer. But it's been passed as an argument to a function, so presumably I've done something weird with references. It looks like it should work - I'm even converting the String to a str, and verifying its type.

use std::io;
use std::num::ParseIntError;
fn print_type_of<T>(_: &T) {print!("{}", std::any::type_name::<T>())} // Unstable.

fn main() {
    let mut guess:String = String::new();
    println!("Please input your guess: ");
    let foo = io::stdin().read_line(&mut guess);
    let bar = re_main(&guess);
}

fn re_main(maybe_number_str: &String) -> Result<i32, ParseIntError> {
    // let maybe_number_str = "1234";  // This is a str, and works
    // let maybe_number: Result<i32, ParseIntError> = maybe_number_str.parse::<i32>();  // Fails
    // let maybe_number: Result<i32, ParseIntError> = maybe_number_str.as_str().parse::<i32>();
    // let maybe_number: Result<i32, ParseIntError> = maybe_number_str.to_string().parse::<i32>();

    // let fubar:&str = "1234";  // This is a str, and works
    // let fubar:&str = &maybe_number_str;           // This claims to be a str, but fails to parse
    // let fubar:&str = &maybe_number_str.as_str();  // This claims to be a str, but fails to parse

    // This block works, so it's clearly parsing a String.
    // let maybe_number_str:String = "1234".to_string();
    // let maybe_number: Result<i32, ParseIntError> = maybe_number_str.parse::<i32>();

    // This block fails to parse?
    let fubar:&str = &maybe_number_str.to_string();
    let maybe_number: Result<i32, ParseIntError> = fubar.parse::<i32>();

    print!("\tmaybe_number.type is:  "); print_type_of(&maybe_number);
    print!("\n\tmaybe_number_str.type is:  "); print_type_of(&maybe_number_str);
    print!("\n\tmaybe_number_str.val is: [{}]", &maybe_number_str);
    print!("\n\tfubar.type is:  "); print_type_of(&fubar);
    print!("\n\tfubar.val is:  [{}]\n\n", &fubar);

    let new_number: i32 = match maybe_number {
        Ok(123)  =>            {println!("wow, I never expected this!"); 123},
        Ok(new_number)  => {println!("everything's fine here, how are you?"); new_number},
        Err(e) =>  {println!("No! How could this happen?"); return Err(e)}
    };

    println!("\nGot Result of some kind, apparently returned: [{}]\n", new_number);
    print!("new_number.type is:  "); print_type_of(&new_number);
    Ok(42i32)
}

So given a String as function-parameter, how do I convert it to int? What am I missing? Anything greatly appreciated.

The .parse::<i32>() method doesn’t like line-breaks, as e.g. demonstrated by

fn main() {
    dbg!("123".parse::<i32>());
    dbg!("123\n".parse::<i32>());
}

You can use .trim().parse::<i32>() to first trim any whitespace before and after the number.

3 Likes

You should also pass references to Strings as &str, not &String:

-fn re_main(maybe_number_str: &String) -> Result<i32, ParseIntError> {
+fn re_main(maybe_number_str: &str) -> Result<i32, ParseIntError> {
  • It accepts more values (like literal strings: "123")
  • It removes a layer of indirection
  • You can't really do anything useful with &String that you can't do with &str
2 Likes

Wow... Line-breaks? I actually saw them in the output, as I like to put brackets around any printed values, to see if they're empty, etc. Just didn't think anything of them - I mean, it works fine in Python. :grinning: Sigh...

Thanks for the help, all.

Oh, yeah, just wondered - is there any way to get more detailed information on the error, other than ParseIntErr?

You can display the error (e.g. in a print) and it will give the description "invalid digit found in string". Beyond that, I don’t think that what or where the invalid character found was is not saved a part of the error struct, so no.

Regarding printing the string, there’s an improvement: you can print

print!("\n\tmaybe_number_str.val is: {:?}", &maybe_number_str);

with the :? to get Debug printing instead of Display; so this will print something like

        maybe_number_str.val is: "123\n"

hence making the presence of the \n more obvious. I think if you see clearly that for "123" works but "123\n", it becomes way more obvious than if the output prints the line-break just as a line-break.

1 Like

Ah, nice, thanks. And I like the formatting-change, that sort of bothered me, just using empty braces. Too much like C, which I've tried hard to forget. :slight_smile:

For completeness let me note that this can also be written

print!("\n\tmaybe_number_str.val is: {maybe_number_str:?}");

Also note that passing maybe_number_str instead of &maybe_number_str to the print doesn’t really matter because the print macro (and other macros with the same format-string syntax) will only access the expressions you pass into by-reference.

3 Likes

As an aside, this is unnecessary:

You already have a &String or a &str to begin with. All this does is allocate a new String, copy the contents over, and coerce it right to a &str. This is just a very expensive no-op, there is no reason to do this.

Totally agree, but in trying to find the bug, I was doing a lot of unnecessary things, breaking everything down as much as possible. Possibly to the point of thrashing. :upside_down_face: