Search code examples
rustenumsmacrosdestructuringrust-macros

destructured items not found when destructuring an enum inside a macro


macro_rules! declare_types {
    ($($token:ident -> $inner:ty, $to_string:expr,)*) => {
        enum Value {
            $($token($inner),)*
        }

        impl Display for Value {
            fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
                match self {
                    $(
                        Self::$token(inner)=>write!(fmt,"{}",$to_string)?,
                    )*
                };
                Ok(())
            }
        }
    }
}

declare_types!(
    String -> String, inner,
);

However, the code results in a compiler error:

cannot find value `inner` in this scope
not found in this scope

When I hover onto inner, IDE shows it properly:

ide hover

Also, by expanding the macro and pasting it back, it works without any errors.


Solution

  • Because of macro hygiene, identifiers created inside the macro (like inner in Self::$token (inner)) are different from identifiers created in the calling code (like inner in declare_types!(String -> String, inner);). If you want to be able to use an identifier in the displayed expression, you will need to pass this identifier to the macro from the caller:

    macro_rules! declare_types {
        ($($token:ident -> $inner:ty, $id: ident, $to_string:expr,)*) => {
            enum Value {
                $($token($inner),)*
            }
    
            impl std::fmt::Display for Value {
                fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
                    match self {
                        $(
                            Self::$token($id)=>write!(fmt,"{}",$to_string)?,
                        )*
                    };
                    Ok(())
                }
            }
        }
    }
    
    declare_types!(
        String -> String, inner, inner,
    );
    

    Playground