Why Does Specific Type Pattern Matching Fail in Nested Rust Macros?

Hello Rust community,

I'm new to writing Rust macros and could use some help with an issue I'm facing. I'm trying to create a nested macro that processes values differently based on their type, but the specific type pattern (e.g., String) fails to match in a nested setup, even though it works fine in a non-nested macro. Below is a minimal example to show the problem, followed by my questions.

Sample Code

Non-Nested Macro (Works as Expected)

macro_rules! single_macro {
    ($value:expr, String) => {
        println!("String value: PREFIX_{}", $value);
    };
    ($value:expr, $ty:ty) => {
        println!("Non-String value: {}", $value);
    };
}

fn main() {
    let s = String::from("hello");
    let i = 42;
    single_macro!(s, String); // Outputs: String value: PREFIX_hello
    single_macro!(i, i32);    // Outputs: Non-String value: 42
}

Nested Macro (Doesn't Work as Expected)

macro_rules! inner_macro {
    ($value:expr, String) => {
        format!("String value: PREFIX_{}", $value)
    };
    ($value:expr, $ty:ty) => {
        format!("Non-String value: {}", $value)
    };
}

macro_rules! outer_macro {
    ($value:expr, $ty:ty) => {
        println!("{}", inner_macro!($value, $ty));
    };
}

fn main() {
    let s = String::from("hello");
    let i = 42;
    outer_macro!(s, String); // Outputs: Non-String value: hello (Expected: String value: PREFIX_hello)
    outer_macro!(i, i32);    // Outputs: Non-String value: 42 (Correct)
}

Problem

In the non-nested macro (single_macro), the ($value:expr, String) pattern correctly matches for the String type, producing String value: PREFIX_hello. However, in the nested macro (outer_macro and inner_macro), the same pattern fails to match for String, and the ($value:expr, $ty:ty) pattern is used instead, resulting in Non-String value: hello.

Questions

  1. Why does the specific type pattern (e.g., String) match correctly in a non-nested macro but fail in a nested macro?
  2. Is there a way to reliably match specific types like String in nested macros?
  3. Are there documented limitations or best practices for matching specific type literals in Rust macros that a beginner should know?

Context

  • Rust version: Latest stable (as of July 2025).
  • Goal: Create a reusable nested macro for type-specific formatting.

Thanks for any insights or advice!

Using the ident fragment specifier instead of the ty fragment specifier will allow the type name to rematch (Playground). However, note that this macro still can’t handle paths like std::string::String or generic arguments (including for non-generic types, String::<> is a valid name way to refer to String), and that type aliases will not resolve to the underlying type before macro expansion. It is very possible to add support for more complex paths and generic arguments to the macro, but I’m not aware of a way to resolve type aliases in a macro.

2 Likes

Most fragments become opaque when forwarded.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.