關(guān)于rust宏-過程宏(補(bǔ)充)

一焊唬、 rust編譯過程

rust編譯過程

從上面的編譯過程圖伴澄,可以看到聲明宏過程宏被編譯到AST中過程是不同的:

  • 聲明宏:通過macro_rule 定義的宏最終只是被解析為TokenStream;
  • 過程宏: 定義的過程宏首先被解析為proc_macro::TokenStream珊皿,接著再被解析為proc_macro2::TokenStream,再使用Syn庫解析為AST(這里的不同于編譯生成的AST)寸齐,最后再通過Quote解析為TokenStream,最終會(huì)被擴(kuò)展到編譯過程TokenStream中,進(jìn)而再被編譯AST;

二脊另、過程宏

1济赎、樣例


屬性過程宏樣例

此處定義一個(gè)屬性過程宏的樣例
2鉴逞、定義過程宏

  • 準(zhǔn)備工作
    主要是Cargo.toml文件中
[lib]
proc-macro=true # 開啟過程宏

# 引入如下三個(gè)crate的原因 參考上面編譯過程圖:在生成最終的TokenStream前,需要依賴這三個(gè)crate
[dependencies]
proc-macro2 = "1.0.34"
syn = {version="1.0.82", feature="full"}
quote ="1.0.10"
  • 項(xiàng)目結(jié)構(gòu)
    過程宏需要定義在一個(gè)單獨(dú)的crate中司训,主要是因?yàn)檫^程宏是一段在編譯crate前构捡,對(duì)其代碼進(jìn)行加工的代碼,而這段是需要在編譯后執(zhí)行的豁遭。若是將定義過程宏和使用過程宏放到同一個(gè)crate中叭喜,就會(huì)陷入編譯“死鎖”:
    1、要編譯的代碼需要運(yùn)行過程宏進(jìn)行展開蓖谢,否則代碼是不完整的,無法編譯crate譬涡;
    2闪幽、不能編譯crate,那么在crate中的過程宏就無法執(zhí)行涡匀,就不能展開被過程宏“修飾”的代碼

  • 定義過程宏

use proc_macro::TokenStream;

/// 定義一個(gè)屬性過程宏
/// 盯腌??陨瘩?輸入?yún)?shù):TokenStream 輸出參數(shù): TokenStream【解釋見`項(xiàng)目結(jié)構(gòu)`】
/// 不做任何加工腕够,直接輸出item
#[proc_macro_attribute]
pub fn my_first_attr_proc_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
    eprintln!("===輸出attr");
    eprintln!(" {:#?}", attr);
    eprintln!("===輸出item");
    eprintln!("{:#?}", item);
    item
}
  • 使用過程宏
    1、Cargo.toml文件引入過程宏crate
[dependencies]
produceral_macro={path= "../procedural_macro_demos/procedural_macro" } # 本地路徑引用

2舌劳、使用過程宏
通過#[crate_name::proc_macro_func("過程宏名字-任意")]
類似 #[produceral_macro::my_first_attr_proc_macro("my_first_procedural_macro")]

#[produceral_macro::my_first_attr_proc_macro("my_first_procedural_macro")]
fn add(a: u64, b: u64) -> () {
    eprintln!("a={:?}, b={:?}, a+b={:?}", a, b , (a+b));
}

rust過程宏本質(zhì)就是一個(gè)編譯環(huán)節(jié)的“過濾器”或者說是一個(gè)“中間件”帚湘,接收一段用戶編寫的源代碼,再做一通“轉(zhuǎn)換”操作甚淡,然后返回給編譯器一段經(jīng)過修改的代碼大诸。

目前過程宏代碼的調(diào)試,最好通過print來進(jìn)行

最終生成的TokenStream是沒有任何語義信息的,是通過樹形結(jié)構(gòu)的數(shù)據(jù)組織资柔,表達(dá)了用戶源代碼各個(gè)語言元素的類型及其相互之間的關(guān)系焙贷;每個(gè)語言元素都有一個(gè)span屬性,記錄該元素在用戶源代碼中的位置贿堰。同時(shí)不同類型的節(jié)點(diǎn)有各自獨(dú)有的屬性辙芍。
而Rust過程宏就是自己能夠手動(dòng)修改輸入變量中的值,比如樣例中的attr,item,換句話說等價(jià)于加工原始輸入代碼羹与,最后將加工后的代碼返回給編譯器即可.

三故硅、proc-macro2\syn\quote三個(gè)包在過程宏中的應(yīng)用

使用syn、quote等crate模擬過程宏“修改”源代碼注簿,并將結(jié)果以TokenStream給到編譯器:

#[proc_macro_attribute]
pub fn my_first_attr_proc_macro_parse(attr: TokenStream, input: TokenStream) -> TokenStream {
    eprintln!("===attr: {:#?}", attr);
    eprintln!("===item: {:#?}", input);
    // 通過syn來解析輸入的TokenStream
    let input_fn = syn::parse_macro_input!(input as syn::ItemFn);
    // 獲取該token對(duì)應(yīng)的類型:ident
    let name = input_fn.sig.ident.clone();
    //  輸出TokenStream給到編譯器
    TokenStream::from(quote! { // 使用quote庫來構(gòu)建編譯器需要的TokenStream
        fn #name () {
            #input_fn

            for i in 0..3 {
                println!("loop time {}", i);

                let r = std::panic::catch_unwind(|| {
                    #name();
                });

                if r.is_ok() {
                    return;
                }
                if i == 2 {
                    std::panic::resume_unwind(r.unwrap_err());
                }
            }
        }
    })
}

測試代碼

#[procedural_macro::my_first_attr_proc_macro_parse]
fn test_proc_macro() {
    assert_eq!(1,1);  // 運(yùn)行正常
    assert_eq!(1,22);  // 觸發(fā)panic
}

四契吉、編譯過程宏代碼

#輸出未進(jìn)行宏擴(kuò)展的ast樹
$ cargo rustc -- -Z ast-json-noexpand=yes
#輸出宏擴(kuò)展后的ast樹
$ cargo rustc -- -Z ast-json=yes 
#輸出hir格式的中間描述
$ cargo rustc -- -Z unpretty=hir 
#輸出hir格式并帶有類型信息的中間描述
$ cargo rustc -- -Z unpretty=hir,typed 
#輸出hir格式并帶有完整樹結(jié)構(gòu)的中間描述
$ cargo rustc -- -Z unpretty=hir-tree
#輸出mir格式的中間描述
$ cargo rustc -- -Z unpretty=mir

#### 代碼編譯中間代碼 ####
#輸出llvm ir格式的中間描述
$ rustc --emit llvm-ir lrfrc.rs
#輸出匯編格式的中間描述
$ rustc --emit asm lrfrc.rs
#分析中間匯編輸出
$ rustc -C opt-level=3 --emit=obj lrfrc.rs
$ size -A lrfrc.o
$ objdump -d lrfrc.o
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市诡渴,隨后出現(xiàn)的幾起案子捐晶,更是在濱河造成了極大的恐慌,老刑警劉巖妄辩,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惑灵,死亡現(xiàn)場離奇詭異,居然都是意外死亡眼耀,警方通過查閱死者的電腦和手機(jī)英支,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哮伟,“玉大人干花,你說我怎么就攤上這事±慊疲” “怎么了池凄?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鬼廓。 經(jīng)常有香客問我肿仑,道長,這世上最難降的妖魔是什么碎税? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任尤慰,我火速辦了婚禮,結(jié)果婚禮上雷蹂,老公的妹妹穿的比我還像新娘伟端。我一直安慰自己,他們只是感情好萎河,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布荔泳。 她就那樣靜靜地躺著蕉饼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪玛歌。 梳的紋絲不亂的頭發(fā)上昧港,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音支子,去河邊找鬼创肥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛值朋,可吹牛的內(nèi)容都是我干的叹侄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼昨登,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼趾代!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丰辣,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤撒强,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后笙什,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體飘哨,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年琐凭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芽隆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡统屈,死狀恐怖胚吁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情愁憔,我是刑警寧澤囤采,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站惩淳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏乓搬。R本人自食惡果不足惜思犁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望进肯。 院中可真熱鬧激蹲,春花似錦、人聲如沸江掩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至策泣,卻和暖如春衙傀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背萨咕。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工统抬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人危队。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓聪建,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茫陆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子金麸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 一、概述 為了解決rust語法元素的擴(kuò)展簿盅,并能復(fù)用現(xiàn)有的代碼挥下,在rust編寫的程序中普遍使用宏.通過宏定義和宏調(diào)用...
    神奇的考拉閱讀 3,960評(píng)論 0 2
  • 編譯過程 整體流程:[源代碼]->分詞->[Tokens詞條流]->解析->[AST]->語法分析,宏擴(kuò)展→[高級(jí)...
    JennerHua閱讀 2,845評(píng)論 0 1
  • 0x00 關(guān)于參數(shù)校驗(yàn) 開題: 在Spring開發(fā)中, 我們習(xí)慣了, 在參數(shù)Bean中添加JSR標(biāo)準(zhǔn)的參數(shù)校驗(yàn)An...
    國服最坑開發(fā)閱讀 2,543評(píng)論 0 3
  • 有沒有同學(xué)記得我們一起挖了多少個(gè)坑挪鹏?嗯…其實(shí)我自己也不記得了见秽,今天我們?cè)賮硗谝粋€(gè)特殊的坑,這個(gè)坑可以說是挖到根源了...
    Jackeyzhe閱讀 745評(píng)論 0 1
  • 來嘞早不如來的巧讨盒,剛翻譯好解取,你就來啦!翻譯完成 要開發(fā)CMS(內(nèi)容管理系統(tǒng))得有個(gè)模板引擎,Tera是使用Rust...
    不安分的程序員天朗閱讀 2,623評(píng)論 0 0