是否可以在过程宏中判断字段是某种类型还是实现某种方法?

时间:2019-04-01 08:53:50

标签: rust rust-proc-macros

我创建了一个实现特征的程序宏,但是为了使其工作,我需要获取每个字段的原始字节。问题是如何获取字段的字节取决于字段的类型而不同。

是否有某种方法可以测试某个功能是否存在于字段中,以及是否不尝试其他功能?

例如像这样的东西:

if item.field::function_exist {
    //do code
} else {
    //do other code
}

目前,我正在考虑创建另一个我必须为所有原语创建的trait / member函数,并为更大的字段(如struct)创建一个过程宏。例如:

if item.field::as_bytes().exists {
    (&self.#index).as_bytes()
} else {
    let bytes = (&self.#index).to_bytes();
    &bytes
}

对于字符串,它具有as_bytes成员函数,而i32没有。这意味着当struct的member字段不是字符串时,我需要额外的代码。我可能需要一个match而不是一个if,但是对于这个示例,if就足够了。

1 个答案:

答案 0 :(得分:2)

  

是否可以在程序宏中判断字段是某种类型还是实现某种方法?

不,不是。

宏在Rust代码的abstract syntax tree (AST)上运行。这意味着您基本上只需要获得用户键入的字符。

如果用户代码具有类似type Foo = Option<Result<i32, MyError>>的内容,并且您处理了一些使用Foo的代码,则宏将不会知道它确实是Option

即使 did 知道类型,要知道可用的方法也会更加困难。未来的板条箱可以创建将方法添加到现有类型的特征。在程序宏运行的时间点,这些包装可能甚至还没有被编译。

  

我正在考虑创建另一个我必须为所有原语创建的trait / member函数,并为诸如struct之类的较大字段创建一个过程宏。

这是正确的解决方案。如果您查看任何现有的良好使用的程序宏,那么它就是这样做的。这使编译器可以执行编译器打算执行的操作。

对于可维护性而言,这也更好—现在,这些原始实现都位于标准的Rust文件中,而不是嵌入到宏内部。更容易阅读和调试。

您的板条箱将具有以下内容:

// No real design put into this trait
trait ToBytes {
    fn encode(&self, buf: &mut Vec<u8>);
}

impl ToBytes for str {
    fn encode(&self, buf: &mut Vec<u8>) {
        buf.extend(self.as_bytes())
    }
}

// Other base implementations

您的程序宏将以简单的方式实现此目的:

#[derive(ToBytes)]]
struct Foo {
    a: A,
    b: B,
}

成为

impl ToBytes for Foo {
    fn encode(&self, buf: &mut Vec<u8>) {
        ToBytes::encode(&self.a);
        ToBytes::encode(&self.b);
    }
}

作为一个具体示例,Serde使用多种序列化二进制数据和从二进制数据串行化的方式做同样的事情: