10

How do I extract two i32s from a single line of input in Rust? In Python I can read two ints like:

a, b = map(int, input().split()) #  "2 3"  =>  a=2 and b=3

As of Rust 1.3.0, I can run the following to read one i32:

let mut s = String::new();
std::io::stdin().read_line(&mut s).ok().expect("read_line panic");
let n: i32 = s.trim().parse().ok().expect("parse panic");
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jay Foreman
  • 600
  • 1
  • 6
  • 17
  • Possible duplicate of [How to split a string in Rust?](http://stackoverflow.com/questions/26643688/how-to-split-a-string-in-rust) – llogiq Nov 10 '15 at 13:00
  • 2
    Have you tried combining `map` and `split` like the Python code? It is expected that you show *some* effort at solving your own problem, Stack Overflow isn't a code writing service. – Shepmaster Nov 10 '15 at 13:23
  • 2
    By the way. As of Rust 1.4.0 (released about two weeks ago), `result.expect("msg")` is available and objectively better than `.ok().expect("msg")` (because it includes the contents of the `Err` in the panic message). –  Nov 11 '15 at 12:53

3 Answers3

11

&str has a split_whitespace() method which returns an iterator yielding parts of the target string separated by an arbitrary amount of whitespace, much like split() in Python. You can use the map() iterator adapter to convert each part to a number:

let mut s = String::new();
std::io::stdin().read_line(&mut s).expect("read_line error");

let mut parts = s.split_whitespace().map(|s| s.parse::<i32>());
match (parts.next(), parts.next()) {
    (Some(Ok(a)), Some(Ok(b))) => {
        // a and b are i32
    }
    // handle other problems: not enough numbers, numbers are invalid, etc
    _ => {}  // ignore invalid input
}

This looks a lot more verbose than the Python version, but that's mostly because in Python all errors will be thrown as exceptions, while in Rust you have to handle them explicitly. If you don't care about them, you can throw match away:

let a = parts.next().unwrap().unwrap();
let b = parts.next().unwrap().unwrap();

Even another approach would be to use a handy collect() implementation for Result<Vec<_>, _>:

let items: Result<Vec<i32>, _> = parts.collect();

This way if any of numbers in the input string fail to parse, items will contain the corresponding Err value, but if they all are parsed successfully, then items will contain Ok variant with the vector of parsed numbers. With this approach you also do not need to specify ::<i32>() in parse() invocation, as it will be inferred automatically (no need for mut either):

let parts = s.split_whitespace().map(|s| s.parse());

Also there is no one-liner function to read a string from stdin in the standard library. It is somewhat unfortunate but rarely a problem in practice. There are libraries which provide such functionality; see other answers to find some examples.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
6

You can use the text_io crate which offers a macro read! to read from standard input to produce tuples:

#[macro_use] extern crate text_io;
let (a, b): (i32, i32);
scan!("{} {}", a, b);

You can also read the values one by one

#[macro_use] extern crate text_io;
let a: i32 = read!();
let b: i32 = read!();
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
oli_obk
  • 28,729
  • 6
  • 82
  • 98
3

The Rust code is always going to be more verbose than the Python one. But since version 1.26, Rust also supports slice patterns as shown below. The code looks more readable in my opinion.

fn main() {
    let a = "2 3";
    if let [Ok(aa), Ok(aaa)] = &a.split(" ")
                                 .map(|a| a.parse::<i32>())
                                 .collect::<Vec<_>>()[..] {
        println!("{:?} {:?}", aa, aaa);
    }
}
trent
  • 25,033
  • 7
  • 51
  • 90
basic_bgnr
  • 697
  • 5
  • 14