2

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]);
     }
}

3 Answers 3

11
+50

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.

Sign up to request clarification or add additional context in comments.

1 Comment

TIL: there is an inspect function! That's very useful indeed to peek in the middle of the stream.
8

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);
}

3 Comments

Thank you! I originally tried to trim the string, but forgot that the function returns a new string, it doesn't manipulate the old one. Thank you very much for your response!
@KavanaughDempsey the function returns a new string — just to be specific, trim returns a new string slice (&str); this refers to the existing string and doesn't allocate a new owned string (String).
Instead of .trim().split(" ") you might also consider .split_whitespace().
5

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();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.