《Rust宏系列教程—自定义派生宏》博客中,我们详细介绍自定义派生宏的过程。但演示例子相对简单,本文在前面基础上实现更复杂、更强大的派生宏示例。并且还提供更好的方法使用迭代器和quote,我在最初的实现中跳过了这一点——这是有意为之,因为它需要我们学习更多的 quote
语法。
前文示例代码优化
在我们深入研究它的工作原理之前,让我们看看它会是什么样子:
let input = syn::parse_macro_input!(item as syn::DeriveInput);
let struct_identifier = &input.ident;
match &input.data {
Data::Struct(syn::DataStruct {
fields, .. }) => {
let field_identifiers = fields.iter().map(
|item| item.ident.as_ref().unwrap()
).collect::<Vec<_>>();
quote! {
impl From<#struct_identifier> for std::collections::HashMap<String, String> {
fn from(value: #struct_identifier) -> Self {
let mut hash_map = std::collections::HashMap::<String, String>::new();
#(
hash_map.insert(
stringify!(#field_identifiers).to_string(),
String::from(value.#field_identifiers));
)*
hash_map
}
}
}
}
_ => unimplemented!()
}.into()
这看起来更简洁,更容易理解!让我们来看看使它成为可能的特殊语法——特别是下面这行:
#(
hash_map.insert( stringify!(#field_identifiers).to_string(), String::from(value.#field_identifiers) );
)*
让我们来分析一下。首先,用#()*将整个代码块包装起来,然后将代码放入括号内。这种语法允许你使用圆括号内的任何迭代器,并且它将为迭代器中的所有项重复该代码块,同时在每次迭代中将变量替换为正确的项。
在本例中,首先创建一个field_identifiers迭代器,它是目标结构中所有字段标识符的集合。然后,在直接使用迭代器时编写hash_map插入语句,就好像它是单个项一样。#()*包装器将其转换为预期的多行输出,每一行代表迭代器中的每一项。
更复杂派生宏示例
我们已经掌握了轻松地编写简单的派生宏技能,接下来,是时候继续创建在现实世界中实际有用的东西了—特别是在使用数据库模型的情况下。
- 构建
DeriveCustomModel<