是否可以判断一个字段是某种类型还是在程序宏中实现某种方法? [英] Is it possible to tell if a field is a certain type or implements a certain method in a procedural macro?

查看:35
本文介绍了是否可以判断一个字段是某种类型还是在程序宏中实现某种方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I created a procedural macro that implements a trait, but in order for this to work I need to get the raw bytes for every field. The problem is how to get the bytes of a field differs depending on the type of field.

是否有某种方法可以测试一个函数是否存在于一个字段中,并且它不尝试另一个函数?

Is there some way of testing if a function exists on a field and if it does not tries another function?

例如像这样:

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

目前我正在考虑创建另一个特征/成员函数,我只需要为所有基元创建该函数,并为结构等更大的字段创建一个过程宏.例如:

Currently I am looking at creating another trait/member function that I just have to create for all primitives and create a procedural macro for larger fields such as structs. For example:

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

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

With a string, it has a as_bytes member function, while i32 does not. This means I need extra code, when the member field of the struct is not a string. I might need a match rather than an if, but the if will suffice for the example.

推荐答案

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

Is it possible to tell if a field is a certain type or implements a certain method in a procedural macro?

不,不是.

宏对 Rust 代码的抽象语法树 (AST) 进行操作.这意味着您基本上只获取用户输入的字符.

Macros operate on the abstract syntax tree (AST) of the Rust code. This means that you basically just get the characters that the user typed in.

如果用户代码中有类似 type Foo = Option> 的内容,并且您处理了一些使用 Foo 的代码,则宏将不会知道它真的"一个 Option.

If user code has something like type Foo = Option<Result<i32, MyError>>, and you process some code that uses Foo, the macro will not know that it's "really" an Option.

即使它确实知道类型,知道哪些方法可用会更加困难.未来的 crate 可以创建向现有类型添加方法的特征.在程序宏运行时,这些 crate 甚至可能还没有被编译.

Even if it did know the type, knowing what methods are available would be even harder. Future crates can create traits which add methods to existing types. At the point in time that the procedural macro is running, these crates may not have even been compiled yet.

我正在考虑创建另一个特征/成员函数,我只需为所有基元创建该函数,并为结构等更大的字段创建一个过程宏.

I am looking at creating another trait/member function that I just have to create for all primitives and create a procedural macro for larger fields such as structs.

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

This is the correct solution. If you look at any existing well-used procedural macro, that's exactly what it does. This allows the compiler to do what the compiler is intended to do.

这对于可维护性来说也更好——现在这些原始实现存在于标准的 Rust 文件中,而不是嵌入在宏中.更容易阅读和调试.

This is also way better for maintainability — now these primitive implementations live in a standard Rust file, as opposed to embedded inside of a macro. Much easier to read and debug.

你的箱子会有这样的东西:

Your crate will have something like this:

// 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

您的程序宏将以简单的方式实现这一点:

And your procedural macro will implement this in the straightforward way:

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

变成

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

举一个具体的例子,Serde 做同样的事情,多种序列化二进制数据:

As a concrete example, Serde does the same thing, with multiple ways of serializing to and from binary data:

这篇关于是否可以判断一个字段是某种类型还是在程序宏中实现某种方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆