1 个不稳定版本
0.1.0 | 2019年7月15日 |
---|
#2133 in 过程宏
18KB
268 行
这是另一个类似于 quote 的Rust准引用库,它提供了 mquote!
宏,旨在提供更好的可读性和可用性。
动机
quote!
宏仅支持常规插入 #a
(并且你不能在这里放入像 my_struct.field
这样的表达式)和重复插入 #(...)*
。
对我来说,这还不够。如果你想条件性地放入一个标记片段,你必须将它与某个变量关联,然后将其插接到 quote!
表达式
let conditional_piece = if having_fun {
quote!(fn funny_method() { ... })
} else {
quote!()
};
quote!(
#conditional_piece
fn regular_method() { ... }
)
你不觉得这可能会很令人困惑吗?特别是如果有很多这样的东西。即使是简单的表达式 my_struct.field
也必须以这种方式处理。
引入模板 mquote!
它支持
因此,你可以重写上述代码
mquote!{
#{if having_fun}
fn funny_method() { ... }
#{endif}
fn regular_method() { ... }
}
这个crate不仅仅关于语法糖!实际上,在复杂情况下使用 mquote!
会有一些性能提升,因为它不会创建多个 TokenStream
并将它们连接起来,而是在单个 TokenStream
中处理所有内容。
该crate包含 mquote!
和 mquote_spanned!
。第一个的用法示例在下一节中。第二个允许你通过以下语法设置生成标记流的范围:mquote_spanned!(span => ...)
。
更多示例
表达式插入
将给定的表达式转换为标记,使用 ToTokens
。
fn put_filter(enabled: bool) -> proc_macro2::TokenStream {
let good_person = Person{ name: "Oleg", age: 20 };
mquote!{
assert!(!#{enabled} || person.name == #{good_person.name}
&& person.age >= #{good_person.age})
}
}
如果 / elif / else
fn define_container(amount: usize) -> proc_macro2::TokenStream {
mquote!{
#{if amount > 1}
struct People(Vec<Person>);
#{elif amount == 1}
struct Human(Person);
#{else}
struct NoneHuman;
#{endif}
}
}
for
fn define_person(fields: Vec<(Ident, Ident)>) -> proc_macro2::TokenStream {
mquote!{
pub struct Person {
#{for (name, ty) in fields}
#{name}: #{ty}
#{endfor}
}
}
}
匹配
fn hardcode_it(var: Ident, value: Option<&str>) -> proc_macro2::TokenStream {
mquote!{
static #var: &str = #{match value}
#{of Some(x) if x.len() > 0}
#{x};
#{of Some(_)}
"case for empty strings";
#{of None}
"default value";
#{endmatch}
}
}
扩展
有时你希望 mquote!
不克隆地消费一个 TokenTree
迭代器。可以使用特殊语法 ^{iterable}
来实现,该语法接受任何 IntoIterator<Item=TokenTree>
。
fn assign_by_ref(stream: TokenStream) -> TokenStream {
let tail = stream.into_iter().drop(5); // here could be something
// more reasonable
mquote!{
let _ = ^{tail}
}
}
转义 #{}
或 ^{}
如果你想要直接使用 #{abc}
或 ^{abc}
,你应该使用双重花括号
fn it_works() {
let tokens = mquote!(#{{abc}} ^{{abc}});
assert_eq!(tokens.to_string(), "# { abc } ^ { abc }")
}
依赖项
~320KB