Rust macro
任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、只有一半功能的、不完全符合规格的、到处都是bug的、运行速度很慢的Common Lisp实现。 -- 《黑客与画家》
Rust中的宏就两大类:对代码模板做简单替换的声明宏(declarative macro)、可以深度定制和生成代码的过程宏(procedural macro)。
声明宏
像 vec![]
、println!
、以及
info!
,它们都是声明宏。
常用的 tracing log 的宏定义(代码):
1 |
|
可以看到,它主要做的就是通过简单的接口,把不断重复的逻辑包装起来,然后在调用的地方展开而已,不涉及语法树的操作。
Rust 的声明宏相比C/C++更加安全,你无法在需要出现标识符的地方出现表达式,也无法让宏内部定义的变量污染宏外部的内容。 如果重复性的代码无法用函数来封装,那么声明宏就是一个好的选择。
1 |
|
使用声明宏时,需要为参数明确类型,可用类型有:
item
,比如一个函数、结构体、模块等。block
,代码块。比如一系列由花括号包裹的表达式和语句。stmt
,语句。比如一个赋值语句。pat
,模式。expr
,表达式。刚才的例子使用过了。ty
,类型。比如 Vec。ident
,标识符。比如一个变量名。path
,路径。比如:foo
、::std::mem::replace
、transmute::<_, int>
。meta
,元数据。一般是在#[...]
和#![...]
属性内部的数据。tt
,单个的 token 树。vis
,可能为空的一个Visibility
修饰符。比如 pub、pub(crate)
过程宏
过程宏有三种:
函数宏(function-like macro):看起来像函数的宏,但在编译期进行处理。
属性宏(attribute macro):可以在其他代码块上添加属性,为代码块提供更多功能。
派生宏(derive macro):为 derive 属性添加新的功能。
proc-macro-workshop: 过程宏参考
宏会为别人理解你的代码,使用你的代码带来额外的负担。由于宏会生成代码,大量使用宏会让你的代码在不知不觉中膨胀,也会导致二进制很大。当一个功能可以用函数表达时,不要用宏。不要过分迷信于编译时的处理,不要把它当成提高性能的手段。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!