rust - 是否可以判断一个字段是某种类型还是在过程宏中实现某种方法?
问题描述
我创建了一个实现特征的过程宏,但为了使其工作,我需要获取每个字段的原始字节。问题是如何获取字段的字节取决于字段的类型。
是否有某种方法可以测试一个函数是否存在于一个字段中并且它是否不尝试另一个函数?
例如这样的:
if item.field::function_exist {
//do code
} else {
//do other code
}
目前,我正在考虑创建另一个特征/成员函数,我只需要为所有原语创建它,并为更大的字段(例如结构)创建一个过程宏。例如:
if item.field::as_bytes().exists {
(&self.#index).as_bytes()
} else {
let bytes = (&self.#index).to_bytes();
&bytes
}
对于字符串,它有一个as_bytes
成员函数,而i32
没有。这意味着当结构的成员字段不是字符串时,我需要额外的代码。我可能需要 amatch
而不是 a if
,但是if
对于这个例子来说就足够了。
解决方案
Is it possible to tell if a field is a certain type or implements a certain method in a procedural macro?
No, it is not.
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.
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
.
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.
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,
}
becomes
impl ToBytes for Foo {
fn encode(&self, buf: &mut Vec<u8>) {
ToBytes::encode(&self.a, buf);
ToBytes::encode(&self.b, buf);
}
}
As a concrete example, Serde does the same thing, with multiple ways of serializing to and from binary data:
- Bincode
- CBOR
- MessagePack
- etc.
推荐阅读
- java - 如何在不更改实时数据库中的downloadUrl的情况下更新android中FirebaseStorage中的位图图像
- servicenow - 如何在 onChange() 上向记录生产者添加小部件弹出窗口?
- web-scraping - 如何通过 Google Sheet Apps Script 从tiprank 中抓取?
- .net - regsvr32 在没有 dotnet 5 sdk 的情况下不会将我的库注册为 COM
- python - 如何在终端python中添加按钮
- php - 如何根据 CategoryName 中选择的选项填充 SubCategoryName?
- python - 从 Django 中的自定义查询集访问相关集?
- python - 大型文本数据集的文本编码
- linux - sudo执行的程序获取错误的DBUS和env
- javascript - 在 componentDidMount() 中存储来自多个 axios get 请求的响应数据