Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

macro_rules! macro takes string literal "...", expands to both "..." and b"..."

Tags:

macros

rust

I would like to write a macro_rules! macro whose arguments are one ident and a list of string literals. The expansion of the macro needs to contain both the string literals, and the corresponding byte literals. The macro is for tests, and will only ever be used with strings containing only characters in the U+0000 ... U+007F range. It is OK if supplying anything other than string literals to the macro causes a compile-time error.

If this isn't currently possible without resorting to procedural macros, just tell me so, don't bother actually writing the procedural macro ;-)

The desired invocation and expansion is like this:

all_s! isalpha [ "abcdefghijklmnopqrstuvwxyz" /* , ... */ ];

=>

assert!(isalpha("abcdefghijklmnopqrstuvwxyz"));
assert!("abcdefghijklmnopqrstuvwxyz".chars().all(|b| isalpha(b));
assert!(isalpha(b"abcdefghijklmnopqrstuvwxyz"));
assert!(b"abcdefghijklmnopqrstuvwxyz".iter().all(|b| isalpha(b)));
/* ... */

This is as far as I've gotten:

macro_rules! all_s {
    ($what: ident, $( $str: tt ),* ) => {{
        $(
            assert!($what($str));
            assert!($str.chars().all(|b| $what(b));
            assert!($what(BYTE_LITERAL!($str)));
            assert!(BYTE_LITERAL!($str).iter().all(|b| $what(b)));
        )*
    }}
}

but I don't know what to put where it says BYTE_LITERAL!, and also I'm getting error messages that suggest that I haven't written the match pattern correctly, e.g. "macro all_s! expects no ident argument, given 'isalpha'" when '$what:ident' is right there.

like image 778
zwol Avatar asked Feb 08 '17 17:02

zwol


1 Answers

You cannot convert a literal to another type of literal; it's just not how macros work.

You can convert a &str to a &[u8] via str::as_bytes:

fn is_alpha<T>(_: T) -> bool { true }

macro_rules! all_s {
    ($what: ident, $str: tt) => {{
        assert!($what($str));
        assert!($str.chars().all(|b| $what(b)));
        assert!($what($str.as_bytes()));
        assert!($str.as_bytes().iter().all(|b| $what(b)));
    }};
}

fn main() {
    all_s!(is_alpha, "abcdefghijklmnopqrstuvwxyz");
}
like image 64
Shepmaster Avatar answered Sep 25 '22 13:09

Shepmaster