Reputation: 8142
I have to match bytes in a &[u8]
and want to simplify my code.
Right now it looks like this:
fn f(s: &[u8]) {
if Some(&b'A') == s.get(5) && Some(&b'X') == s.get(16) &&
(Some(&b'B') == s.get(17) || Some(&b'C') == s.get(18)) {
} else if Some(&b'1') == s.get(4) && Some(&b'2') == s.get(7) && Some(&b'D') == s.get(10) {
}
}
I know about nom, but it does not make this particular case simpler, although I'm using nom afterwards to extract the data after the match.
So my first attempt to simplify code is to write these macros:
macro_rules! m {
($ch:expr, $pos:expr) => {{
Some(&$ch) == line.get($pos)
}};
($ch1:expr, $ch2:expr, $pos:expr) => {{
Some(&$ch1) == line.get($pos) || Some(&$ch2) == line.get($pos)
}}
}
It reduces code size, and decreases the possibility of making a mistake, but I want more:
macro_rules! match_several {
($($ch:expr, $pos:expr),*, $last_ch:expr, $last_pos:expr) => {{
(Some(&$ch) == line.get($pos) &&)* Some(&$last_ch) == line.get($last_pos)
}}
}
But the compiler gives this error:
error: local ambiguity: multiple parsing options: built-in NTs expr ('last_ch') or expr ('ch').
--> lib.rs:45:32
|
45 | if match_several!(b'P', 4, b' ', 5, b'A', 12) {
| ------------------------^^^^-------------- in this macro invocation
Test code:
let line: &'static [u8] = b"0123P 6789ABA";
println!("match result {:?}", match_several!(b'P', 4, b' ', 5, b'A', 12));
Upvotes: 2
Views: 394
Reputation: 3861
Repetitive patterns in this case could be abstracted as somewhat useful functions.
#[inline]
fn all_match(s: &[u8], pattern: &[(u8, usize)]) -> bool {
pattern.into_iter().all(|&(b, p)| s.get(p) == Some(&b))
}
#[inline]
fn any_match(s: &[u8], pattern: &[(u8, usize)]) -> bool {
pattern.into_iter().any(|&(b, p)| s.get(p) == Some(&b))
}
fn f(s: &[u8]) {
let and_pattern = [(b'A', 5), (b'X', 16)];
let or_pattern = [(b'B', 17), (b'C', 18)];
if all_match(s, &and_pattern)
&& any_match(s, &or_pattern) {
} else if all_match(s, &[(b'1', 4), (b'2', 7), (b'D', 10)]) {
}
}
Upvotes: 3
Reputation: 59125
Change the macro to:
macro_rules! match_several {
($($ch:expr, $pos:expr),*) => {{
$(Some(&$ch) == line.get($pos))&&*
}}
}
Note that I'm using &&
as the separator, and I got rid of $last_ch
and $last_pos
. This works because you can use any token other than +
or *
as a separator, and &&
is a single token.
Upvotes: 4