寫了一堆CC++的文章,是時候換換口味了
Macro在Rust也有,不過不同於C/C++的Texture Replace
Rust的Macro強大的不得了,順便也跟C++的template做個比較
Declarative Macros
從min開始
C macro版的min或是C++ templaate版的就不提供了,寫到不想寫了
直接看Rust的
1 | macro_rules! min { |
這樣看起來沒什麼特別的
那如果多加一個變數呢
min version2
1 | macro_rules! min { |
同樣的macro,可以有兩種不同的使用方式意
C語言的marco板本長這樣
1 |
|
看起來就是一堆亂七八糟拼湊的組合怪
來看看Template版
1 | template <typename T> |
憑藉於Function overloading,可讀性高很多,唯一比較麻煩的是要寫兩次template function declaration
min version3
來個varadic個版本,先寫個看起來沒問題,實際上編譯不過的
1 | macro_rules! min { |
後來發現Rust Macro裡面不能有local variable,只能改成這樣
1 | macro_rules! min { |
之後又發現一點和C/C++ preprocessor不同的地方,由於他是直接對AST做操作,所以得到的Token要自己Parse
所以做個實驗,參數之間分隔用;
取代,
,這樣是合法的
1 | macro_rules! min { |
不過沒辦法用local variable有點可惜,
Marco版的,我寫不出來,直接看Variadic Template的版本
1 | template <typename T, typename... Args> |
可以做更多的變化,不過Variadic Template最大的問題是我永遠記不住...
到底要放哪這件事`
不過Rust真正厲害的是第二種Macro
Procedural Macros
基本上就是把輸入的TokenStream
轉成另外的TokenStream
的流程
分成三種
1 | $ cargo new macro-demo --lib |
在Cargo.toml
新增以下兩行
1 | [lib] |
Attribute macros
1 |
|
How to use atribute macro
1 |
|
Function-like procedural macros
1 |
|
How to use function-like macro
1 | seq! { n in 0..10 { |
Derive macro helper attributes
1 |
|
How to use derived macro
1 |
|
Caution
Procedural Macros
不同於Declarative Macros
,必須單獨是一個crate存在,目前IDE對Proc Macro的支持度不好,連Debug Proc Macro也很麻煩,最常使用的還是print大法
Simple example
從別人的範例中學來的,這邊實作一個Attribute macros
1 | $ mkdir rust_proc_macro_demo && cd rust_proc_macro_demo |
修改proc_macro_define_crate/Cargo.toml
加入
1 | [lib] |
接著修改rust_proc_macro_guide/Cargo.toml
1 | [dependencies] |
置換掉proc_macro_define_crate/src/lib.rs
裡面的內容
1 | use proc_macro::TokenStream; |
一樣將rust_proc_macro_guide/src/main.rs
內部的內容換掉
1 | use proc_macro_define_crate::mytest_proc_macro; |
接著用cargo check
檢查
1 | $ cd rust_proc_macro_guide/ |
可以看到類似這樣的輸出
1 | Attr TokenStream [ |
這樣我們就能看出Attr和Item分別對應的TokenStream了
From TokenStream to Syntax Tree
有些時候,光看Lexer的TokenStream無助於解決問題,我們需要Syntax Tree
因此我們修改mytest_proc_macro
1 | use proc_macro::TokenStream; |
會跑出這樣的結果
1 | Attr [ |
Comparsion with C/C++
要達到類似的功能,除了X-Macros之外,我想不到類似的方法了
不過X-Marcos不僅醜,功能還有限,Debug更困難
Reference
– Rust Macro 手册
– Rust宏编程新手指南【Macro】
– Rust 过程宏 101
– The Little Book of Rust Macros
– Rust Latam: procedural macros workshop
– Macros in Rust: A tutorial with examples - LogRocket Blog
– Overloading Macro on Number of Arguments