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 {