Kavanaugh Dempsey
Kavanaugh Dempsey

Reputation: 35

Rust not properly reading integer input

I'm trying to test out my Rust skills with a simple program that reads multiple integers from a single line of input. It compiles correctly, but unfortunately when it receives the input of 1 2 3, it panics, saying that the input wasn't a valid integer. Can someone please explain the reason for this, and also provide an explanation as to how I can fix my program?

use std::io;

fn main() {
    let mut string = String::new();
    io::stdin().read_line(&mut string);

    let int_vec: Vec<u32> = string.split(" ")
        .map(|x| x.parse::<u32>().expect("Not an integer!"))
        .collect();

     for i in (0..int_vec.len()).rev() {
         print!("{} ", int_vec[i]);
     }
}

Upvotes: 2

Views: 826

Answers (3)

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88576

You ran into the old problem of the terminating line-ending. Let's try putting

println!("{:?}", string); 

in the third line of your code. For the input 1 2 3 it will print (on Windows):

"1 2 3\r\n"

So at some point you are trying to parse "3\r\n" as integer, which obviously fails. One easy way to remove trailing and leading whitespace from a string is to use trim(). This works:

let int_vec: Vec<_> = string.trim().split(" ")
    .map(|x| x.parse::<u32>().expect("Not an integer!"))
    .collect();

Upvotes: 5

Dogbert
Dogbert

Reputation: 222108

This is because io::stdin().read_line(&mut String) also adds a trailing newline character to the string, which causes the last str after splitting with " " to be "123\n", which is not a valid integer. You can use str::trim() for this:

use std::io;

fn main() {
    let mut string = String::new();
    io::stdin().read_line(&mut string);

    let int_vec: Vec<u32> = string.trim()
        .split(" ")
        .map(|x| {
            x.parse::<u32>()
                .expect("Not an integer!")
        })
        .collect();

    for i in (0..int_vec.len()).rev() {
        print!("{} ", int_vec[i]);
    }
}

With this change, the program works:

$ ./a
1 2 3
3 2 1

Also, you can simplify your for loop:

for i in int_vec.iter().rev() {
    print!("{} ", i);
}

Upvotes: 8

Simon Whitehead
Simon Whitehead

Reputation: 65059

In addition to Dogberts answer... it might be helpful to see how you might be able to debug this sort of issue with an iterator yourself in future.

The Iterator trait exposes an inspect function that you can use to inspect each item. Converting your code to use inspect both before and after each map results in:

let int_vec: Vec<u32> = string.split(" ")
.inspect(|x| println!("About to parse: {:?}", x))
.map(|x| {
    x.parse::<u32>()
        .expect("Not an integer!")
})
.inspect(|x| println!("Parsed {:?} successfully!", x))
.collect();

Outputs:

1 2 3
About to parse: "1"
Parsed 1 successfully!
About to parse: "2"
Parsed 2 successfully!
About to parse: "3\n"

thread '<main>' panicked at 'Not an integer!...

Notice what its attempting to parse when it gets to the number 3.

Of course, you can inspect string all by itself. inspect is handy though for when iterators are involved.

Upvotes: 11

Related Questions