Rust Lang Book Ch.19 Function Pointers, Returing Closures, macros

Function Pointers

fn类型与Fn特性不一样,fn被称为function pointer,使用方法和Fn相似。但是在与C的FFI交互的时候,只能用fn。

fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(add_one, 5);

    println!("The answer is: {}", answer);
}

  

这里用了fn ToString::to_string而不是closure: |i|i.to_string()

    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> =
        list_of_numbers.iter().map(ToString::to_string).collect();

  

Returing Closure

可以直接返回fn作为返回值,但是若要返回Closure,因为Closure的大小是未定的,所以只能用Box<dyn 包裹。

fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

  

Macros

宏有三类:

1. #[derive],允许structs和enum上自动添加写好的代码

2. Attribute-like:在任意item上自定义属性

3. function-like:虽然像是函数,但是用token作为参数。

 

declarative macros

declarative macros允许编写一些类似match表达式的宏,以vec!的定义为例:

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
//这里有一个模式,如果被匹配,那么就会执行后面的代码快
//$x:expr匹配Rust的任意表达式 { let mut temp_vec = Vec::new(); $( temp_vec.push($x); )* temp_vec } }; }

  

#[macro_export]:如果没有这个注解,该宏就不能被用在作用域中

( $( $x:expr ),* ) =>。如果模式匹配,该相关代码块将被执行。$() 之后的逗号说明一个可有可无的逗号分隔符可以出现在 $() 所匹配的代码之后。紧随逗号之后的 * 说明该模式匹配零个或更多个 * 之前的任何模式。

procedural macros

更像函数,而不是直接替换。derive, attribute-like macros和function-like macros都是procedural macros。

derive

可以定义一个derive_a_macro库。

例如hello_macro。

1. 为自定义派生宏crate: hello_macro_derive的Cargo.toml添加必要依赖库’

[lib]
proc-macro = true#编译器用来读取和操作Rust代码

[dependencies]
syn = "0.14.4"#将代码解析为AST
quote = "0.6.3"#将AST转化为Rust代码

2.   构建AST树,从树中取元素,构造新元素,写入AST树,转为rust代码

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();

    // 构建 trait 实现
    impl_hello_macro(&ast)
}

fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        impl #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}", stringify!(#name));
            }
        }
    };
    gen.into()
}

  

3. 使用

use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Pancakes;

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

 

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

 

 

 

Attribute-like macros

这些宏允许建立新的attribute,此外,attribute-like macros可以对函数,structs,enum都生效。

使用示例

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

  

定义示例

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

  

Function-like macros

宏的好处在于可以使用未知数量的参数,但是,只能遵循宏的语法。这些宏的输入是TokenStream,输出也是TokenStream,不过可以使用Rust代码来操作这些token。

使用示例

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

 

定义示例

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

  

posted @ 2020-10-28 01:46  雪溯  阅读(98)  评论(0编辑  收藏  举报