I've just started learning Rust. I figured I'll solve some problems on HackerRank. I quickly ran into an issue: parsing input from stdin is rather tedious.
Your straightforward approach could do some work, as it has quite a bit of code repetition. The way I'd do this would be like this:
let mut a_str = String::new();
io::stdin().read_line(&mut a_str).expect("read error");
let mut vec = a_str.split_whitespace()
.map(|x| x.parse::<i32>().expect("parse error"))
.collect::<Vec<i32>>();
It removes some of the code duplication through a better use of iterators, though this is still longer than the C++ version. It's less error-prone than the C++ version though, as it will crash if the input is not a number.
If you want to be more concise, you can indeed write a macro as you suggested:
macro_rules! parse_several {
($s:expr => ($($t:ty),*)) => {{
let s = $s.to_owned();
let mut iter = s.split_whitespace();
parse_several!(GENSYMS iter [$([$t])*] [])
}};
// for n types, generate n variables all uniquely named "x" :P
(GENSYMS $iter:ident [] $prepared:tt) =>
{{ parse_several!(READEM $iter $prepared) }};
(GENSYMS $iter:ident [[$t:ty] $($ts:tt)*] [$($prepared:tt)*]) =>
{{ parse_several!(GENSYMS $iter [$($ts)*]
[$($prepared)* ([$iter] [x] [$t])]
) }};
(READEM $iter:ident [$(([$i:ident] [$x:ident] [$t:ty]))*]) => {{
// start parsing
$(
let $x: $t = $i.next().unwrap().parse().expect("parse error");
)*
// make sure nothing is left
if $iter.next().is_some() {
panic!("too many words in string!");
}
// warning: due to a bout of laziness, this returns a regular
// single value instead of a 1-tuple when given one object.
// because nyeah
($($x),*)
}};
}
fn main() {
let (a,b,c) = parse_several!(String::from("232 53 45.2") => (i32, i32, f32));
println!("{:?}", (a,b,c));
println!("this next one should panic...");
let (a,b) = parse_several!(String::from("232 53 45") => (i32, i32));
println!("{:?}", (a,b));
}
(I didn't realize that the borrow checker permitted an expression like (a.mutating_method(), a.mutating_method()), so I put all this extra work into naming temporary variables ...)
Two things to note from there; both mine and @Arios16's macro return a single value instead of a 1-tuple when parsing one item. In general, writing a macro to correctly handle both 0-tuples and 1-tuples is tricky because (,) is an invalid 0-tuple and (a) is an invalid 1-tuple. (Not even itertools::izip! bothers to get them both right!) (actually izip! is a bad example, because it does this deliberately. Weird...)
Also, call me paranoid, but if I expect an iterator to have no more items, I like to check that condition as well (as I do in my macro).