Skip to content

Latest commit

 

History

History
176 lines (128 loc) · 4.61 KB

18.函数与宏.md

File metadata and controls

176 lines (128 loc) · 4.61 KB

高级函数与闭包

示例:

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);
    String::from("hello");
    println!("The answer is: {}", answer);
}
  1. 不同于闭包,fn 是一个类型而不是一个 trait,所以直接指定 fn 作为参数而不是声明一个带有 Fn 作为 trait bound 的泛型参数。
  2. 函数指针实现了所有三个闭包 trait(Fn、FnMut 和 FnOnce),所以总是可以在调用期望闭包的函数时传递函数指针作为参数。
  3. 因为闭包是 trait,需要使用 DST
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

宏与函数区别

在一个文件里调用宏 之前 必须定义它,或将其引入作用域,而函数则可以在任何地方定义和调用。

声明宏

Rust 最常用的宏形式是 声明宏(declarative macros)。它们有时也被称为 “macros by example”、“macro_rules! 宏” 或者就是 “macros”。

原理:
宏将一个值和包含相关代码的模式进行比较;此种情况下,该值是传递给宏的 Rust 源代码字面值,模式用于和传递给宏的源代码进行比较,同时每个模式的相关代码则用于替换传递给宏的代码。所有这一切都发生于编译时。

举例:

使用 vec! 宏来生成一个给定值的 vector:

let v: Vec<u32> = vec![1, 2, 3];

vec! 稍微简化的定义:

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

注意:标准库中实际定义的 vec! 包括预分配适当量的内存的代码。这部分为代码优化,为了让示例简化,此处并没有包含在内。

说明:

  1. #[macro_export] 注解说明宏应该是导入的,用 macro_rules! 和宏名称开始宏定义,且所定义的宏并 不带 感叹号。名字后跟大括号表示宏定义体,在该例中宏名称是 vec 。

  2. vec! 宏的结构和 match 表达式的结构类似。此处有一个单边模式 ( $( $x:expr ),* ) ,后跟 => 以及和模式相关的代码块。如果模式匹配,该相关代码块将被执行。假设这是这个宏中唯一的模式,则只有这一种有效匹配,其他任何匹配都是错误的。更复杂的宏会有多个单边模式。

  3. 模式片段什么意思?首先,一对括号包含了整个模式。接下来是美元符号( $ ),后跟一对括号,捕获了符合括号内模式的值以用于替换后的代码。$() 内则是 $x:expr ,其匹配 Rust 的任意表达式,并将该表达式记作 $x$() 之后的逗号说明一个可有可无的逗号分隔符可以出现在 $() 所匹配的代码之后。紧随逗号之后的 * 说明该模式匹配零个或更多个 * 之前的任何模式。

  4. 替换该宏调用所生成的代码会是下面这样:

let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec

过程宏

derive 宏

示例:

文件名: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();

    // 构建 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()
}

另一个项目中,添加依赖

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

文件名:src/main.rs

use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Pancakes;

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

类属性宏

定义宏:

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

使用宏:

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

类函数宏

定义宏:

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

使用宏:

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