Reputation: 113
I am writing a macro to parse some structured text into tuples, line by line. Most parts work now, but I am stuck at forming a tuple by extracting/converting Strings from a vector.
// Reading Tuple from a line
// Example : read_tuple( "1 ab 3".lines()
// ,(i32, String, i32))
// Expected : (1, "ab", 3)
// Note:: you can note use str
macro_rules! read_tuple {
(
$lines :ident , ( $( $t :ty ),* )
)
=> {{
let l = ($lines).next().unwrap();
let ws = l.trim().split(" ").collect::<Vec<_>>();
let s : ( $($t),* ) = (
// for w in ws {
// let p = w.parse().unwrap();
// ( p) ,
// }
ws[0].parse().unwrap(),
ws[1].parse().unwrap(),
//...
ws[2].parse().unwrap(),
// Or any way to automatically generate these statments?
);
s
}}
}
fn main() {
let mut _x = "1 ab 3".lines();
let a = read_tuple!( _x, (i32, String, i32));
print!("{:?}",a);
}
How can I iterate through ws
and return the tuple within this macro?
You can try here
Upvotes: 4
Views: 4314
Reputation: 90782
A tuple is a heterogeneous collection; each element may be of a different type. And in your example, they are of different types, so each parse
method is needing to produce a different type. Therefore pure runtime iteration is right out; you do need all the ws[N].parse().unwrap()
statements expanded.
Sadly there is not at present any way of writing out the current iteration of a $(…)*
(though it could be simulated with a compiler plugin). There is, however, a way that one can get around that: blending run- and compile-time iteration. We use iterators to pull out the strings, and the macro iteration expansion (ensuring that $t
is mentioned inside the $(…)
so it knows what to repeat over) to produce the right number of the same lines. This also means we can avoid using an intermediate vector as we are using the iterator directly, so we win all round.
macro_rules! read_tuple {
(
$lines:ident, ($($t:ty),*)
) => {{
let l = $lines.next().unwrap();
let mut ws = l.trim().split(" ");
(
$(ws.next().unwrap().parse::<$t>().unwrap(),)*
)
}}
}
A minor thing to note is how I changed ),*
to ,)*
; this means that you will get ()
, (1,)
, (1, 2,)
, (1, 2, 3,)
, &c. instead of ()
, (1)
, (1, 2)
, (1, 2, 3)
—the key difference being that a single-element tuple will work (though you’ll still sadly be writing read_tuple!(lines, (T))
).
Upvotes: 4