Scoping use declaration inside macro

use std::result;
use std::str::FromStr;

Obviously the above two use statements are coming twice when macro is invoked twice, how do I avoid it?

macro_rules! sql_string_enum {
( $enumname: ident {
    $($enumval: ident,)*
    } ) => {
    
        use std::result;
        use std::str::FromStr;
        
        #[derive(Debug)]
        pub enum $enumname {
            $($enumval,)*
        }
        
        impl FromStr for $enumname {
            type Err = &'static str;

            fn from_str(s: &str) -> result::Result<Self, Self::Err> {
                match s {
                
                    //FIXME: This is where I need to enum identifiers 
                    $( stringify!($enumval) => Ok($enumname::$enumval),)*
                    _ => {
                        let msg = stringify!(Not a valid $enumname value);
                        Err(msg)
                    },
                }
            }
        }
        
    }
}

fn main() {
    sql_string_enum!(Resource { ROLE, USER, });
    sql_string_enum!(Access { READ, WRITE, });

    println!("{:?}", Resource::from_str("rere").unwrap());
}
1 Like

Put it outside to avoid repetition.

use std::str::FromStr;

macro_rules! sql_string_enum {
( $enumname: ident {
    $($enumval: ident,)*
    } ) => {

        #[derive(Debug)]
        pub enum $enumname {
            $($enumval,)*
        }
        
        impl FromStr for $enumname {
            type Err = &'static str;

            fn from_str(s: &str) -> Result<Self, Self::Err> {
                match s {
                    //FIXME: This is where I need to enum identifiers 
                    $( stringify!($enumval) => Ok($enumname::$enumval),)*
                    _ => {
                        let msg = stringify!(Not a valid $enumname value);
                        Err(msg)
                    },
                }
            }
        }
        
    }
}

sql_string_enum!(Resource { ROLE, USER, });
sql_string_enum!(Access { READ, WRITE, });

fn main() {
    println!("{:?}", Resource::from_str("USER").unwrap());
}

That does not work as the macro is in a different file.

But I can keep the use statement completely of macro and keep them where it is used. That works but a bit uglier.

1 Like

Common solution, as I see reading rust code, is using full names,
like

impl ::std::str::FromStr for $enumname

so why not just not use using ?

Serde puts it's generated impls into a zero sized const:

#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _IMPL_SERIALIZE_FOR_Newtype: () =
    {
        extern crate serde as _serde;
        #[automatically_derived]
        impl _serde::Serialize for Newtype {
            ....
        }
    };

Edit: I just remember that concating identifiers is impossible. So it is impossible to generate unique names for the consts. I think the best idea is to use Dushistov method.