Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust macro does not match passed types

Tags:

macros

rust

Types passed directly to macros pattern match the way you would expect, but if they're passed through another macro as ty, they stop matching:

macro_rules! mrtype {
    ( bool )   => ("b");
    ( i32 )    => ("i");
    ( f64 )    => ("f");
    ( &str )   => ("z");
    ( $_t:ty ) => ("o");
}

macro_rules! around {
    ( $t:ty ) => (mrtype!($t));
}

fn main() {
    println!("{}{}{}", mrtype!(i32), around!(i32), around!(&str));
}

This prints ioo instead of iiz.

Passing tts instead of tys works, but if you have &str you need 2 tts, making everything unnecessarily complicated.

like image 977
dragostis Avatar asked Feb 21 '16 12:02

dragostis


People also ask

How does Rust macro work?

Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang ! , but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program.

Can you use macros in Rust?

The most widely used form of macros in Rust is the declarative macro. These are also sometimes referred to as “macros by example,” “ macro_rules! macros,” or just plain “macros.” At their core, declarative macros allow you to write something similar to a Rust match expression.

When to use macros vs functions Rust?

Another important difference between macros and functions is that you must define macros or bring them into scope before you call them in a file, as opposed to functions you can define anywhere and call anywhere. Save this answer.

Can a macro return a value Rust?

No, macros operate on syntax only. If there is a matcher that is ($arg:expr, &str) , the latter half of that expects a literal &str to be provided — it does not indicate that $arg is a string.


1 Answers

This doesn't work and cannot be made to work.

To summarise the Captures and Expansion Redux chapter of The Little Book of Rust Macros: the problem is that with the exception of tt and ident captures, macro_rules! is completely unable to destructure or examine captured tokens. Once you capture something as ty, it irrevocably becomes a black box to macro_rules!.

To put it another way: &str is not a type, as far as macro_rules! is concerned: it's two tokens, & and str. When you capture and then substitute &str as ty, though, it becomes a single "meta token": the type &str. The two are no longer the same, and as such don't match.

If you intend to later match against or destructure tokens, you must capture them as tts or idents (if possible). In this specific case, you could rewrite the rule for around to instead be ($($t:tt)*) => (mrtype!($($t)*));, which preserves the original token sequence.

like image 126
DK. Avatar answered Nov 15 '22 07:11

DK.