如何处理或测试类型是否为 Rust 宏中的选项?

How can I handle or test if a type is an Option in a Rust macro?

我正在尝试创建一个宏来生成一个可以从 postgres 数据库填充的结构。 现在,由于数据库中有可为空和不可为空的字段,我想在宏中以不同方式处理它们。

输出应该是这样的结构:

#[derive(Debug, Default)]
pub struct MyStruct {
    pub attrib_a: i64,
    pub attrib_b: Option<i64>,
}

impl MyStruct {
    pub fn get_row(&self, row: &postgres::rows::Row) -> MyStruct {
        MyStruct {
            // the non-nullable attrib_a, where I can for sure take the value out of the Option and assign it
            attrib_a: match row.get::<_, Option<i64>>("attrib_a") {
                Some(x) => x,
                None => 0,
            },

            // here for the nullable attrib_b I just want to return the Option as is
            attrib_b: row.get::<_, Option<i64>>("attrib_b"),
        }
    }
}

这是当前的宏代码:

macro_rules! make_table_struct {
    ($tname: stmt => $sname: ident; $($fname: ident: $ftype: ty),+) => {

        #[derive(Debug, Clone, Default)]
        pub struct $sname {
            $(
                pub $fname: $ftype,
            )+
        }

        impl $sname
        {
            pub fn get_row(&self,row:&postgres::rows::Row)->$sname
            {
                $sname
                {
                    //How do I know if I have an Option here or not and act then accordingly?
                    $(
                        $fname: row.get::<_,Option<$ftype>>(stringify!($fname)),
                    )+
                }
            }
        }
    }
}

宏调用:

make_table_struct! ("source_table_name" => MyStruct; attrib_a: i64, attrib_b: Option<i64>)

这是一个带有 $fname 和类型的宏,这可能是可选的。如果类型是可选的,它会生成另一个函数,如果不是:

macro_rules! make_get_row {
    ($fname: ident, Option<$ftype: ty>) => {
        fn $fname(i: Option<$ftype>) -> i64 {
            i.unwrap_or(0)
        }
    };
    ($fname: ident, $ftype: ty) => {
        fn $fname(i: $ftype) -> i64 {
            i
        }
    };
}

make_get_row!(direct, i64);
make_get_row!(via_op, Option<i64>);

fn main() {
    direct(1);
    via_op(Some(1));
}

您应该能够在 make_table_struct.

中使用该宏的一个(可能经过调整的)变体

以下内容并非制作质量,但可能会带您到某个地方:

macro_rules! get_table_row {
    ($row: ident, nonnullable $fname:ident: $ftype:ty) => {
        $row.get::<_,$ftype>(stringify!($fname))
    };
    ($row: ident, nullable $fname:ident: $ftype:ty) => {
        match $row.get::<_,Option<Option<$ftype>>>(stringify!($fname)) { Some(x) => x, None => Some(0) }
    }
}
type nullable<T> = Option<T>;
type nonnullable<T> = T;
macro_rules! make_table_struct {
    ($tname:stmt => $sname:ident; $($nul: ident $fname:ident: $ftype:tt),+) => {

        #[derive(Debug, Clone, Default)]
        pub struct $sname {
            $(
                pub $fname: $nul < $ftype >,
            )+
        }

        impl $sname {
            pub fn get_row(&self, row: &postgres::rows::Row) -> $sname {
                $(
                    let $fname = get_table_row!(row, $nul $fname: $ftype);
                )+
                $sname {
                    $($fname,)+
                }
            }
        }
    }
}

make_table_struct! ("source_table_name" => MyStruct; nonnullable attrib_a: i64, nullable attrib_b: i64);