Rust 程式設計影片教程(進階)——029_3 過程宏

linghuyichong發表於2020-02-26

頭條地址:https://www.ixigua.com/i677586170644791348...
B站地址:https://www.bilibili.com/video/av81202308/

github地址:https://github.com/anonymousGiga/learn_rus...

1、過程宏介紹
過程宏接收 Rust 程式碼作為輸入,在這些程式碼上進行操作,然後產生另一些程式碼作為輸出,而非像宣告式宏那樣匹配對應模式然後以另一部分程式碼替換當前程式碼。
定義過程宏的函式接受一個 TokenStream 作為輸入併產生一個 TokenStream 作為輸出。這也就是宏的核心:宏所處理的原始碼組成了輸入 TokenStream,同時宏生成的程式碼是輸出 TokenStream。如下:

use proc_macro; 
#[some_attribute] 
pub fn some_name(input: TokenStream) -> TokenStream { 
}

2、自定義derive宏
(1)mkdir learn_marco2
(2)cargo new hello_macro –lib
(3)cd hello_macro ,編輯src/lib.rs如下:

pub trait HelloMacro {
    fn hello_macro();
}

(4)cargo new hello_macro_derive –lib
(5)編輯hello_macro_derive/Cargo.toml新增如下:

[lib]
proc-macro = true
[dependencies]
syn = "0.14.4"
quote = "0.6.3"

(6)編輯hello_macro_derive/src/lib.rs如下:

extern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // 構建 Rust 程式碼所代表的語法樹
    // 以便可以進行操作
    let ast = syn::parse(input).unwrap();//解析出DeriveInput結構體
    // 構建 trait 實現
    impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}", stringify!(#name));
            }
        }
    };
    gen.into()
}

(7)使用宏:cd ..
(8)cargo new pancakes
(9)編輯pancakes/Cargo.toml如下:

[dependencies]
hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }

(10)編輯pancakes/src/main.rs如下:

use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro();
}

說明:在hello_macro_derive函式的實現中,syn 中的 parse_derive_input 函式獲取一個 TokenStream 並返回一個表示解析出 Rust 程式碼的 DeriveInput 結構體(對應程式碼syn::parse(input).unwrap();)。該結構體相關的內容大體如下:

DeriveInput { 
    // --snip-- 
    ident: Ident { 
        ident: "Pancakes", 
        span: #0 bytes(95..103)
    }, 
    data: Struct( 
        DataStruct { 
            struct_token: Struct, 
            fields: Unit, 
            semi_token: Some( Semi )
        }
    ) 
}

3、類屬性宏
類屬性宏與自定義派生宏相似,不同於為 derive 屬性生成程式碼,它們允許你建立新的屬性。
例子:
可以建立一個名為 route 的屬性用於註解 web 應用程式框架(web application framework)的函式:

#[route(GET, "/")] fn index() {

#[route] 屬性將由框架本身定義為一個過程宏。其宏定義的函式簽名看起來像這樣:

#[proc_macro_attribute] pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {

說明:類屬性宏其它工作方式和自定義derive宏工作方式一致。

4、類函式宏
類函式宏定義看起來像函式呼叫的宏。類似於 macro_rules!,它們比函式更靈活。
例子:
如sql!宏,使用方式為:

let sql = sql!(SELECT * FROM posts WHERE id=1);

則其定義為:

#[proc_macro] pub fn sql(input: TokenStream) -> TokenStream {

5、宏的資料推薦
https://danielkeep.github.io/tlborm/book/m...

本作品採用《CC 協議》,轉載必須註明作者和本文連結
令狐一衝

相關文章