Rust提高篇 —— 闭包作参数时,三种泛型类型的选择

在Rust中,大部分类型是可以自动推断的,但是在编写函数时不可以,因此需要明确指出函数入参和返回值的类型。

当使用匿名函数——闭包,作为函数的参数时,也需要使用泛型以及详细的Trait bound进行约束,指出闭包的完整类型,一般有以下几个Trait进行选择,且受限程度递减(参考上一篇文章中的“实例方法 instance method”部分):

  • Fn:表示捕获方式为通过引用(&T)的闭包
  • FnMut:表示捕获方式为通过可变引用(&mut T)的闭包
  • FnOnce:表示捕获方式为通过值(T)的闭包

译注:顺序之所以是这样,是因为 &T 只是获取了不可变的引用,&mut T 则可以改变变量,T 则是拿到了变量的所有权而非借用。

闭包会捕获要使用的外部变量,根据要使用的变量的操作类型,选择适合的Trait。

  • 例如值读取值,可以使用Fn
  • 如果要修改值,使用FnMut
  • 如果要获取值的所有权,使用FnOnce,具体使用场景是销毁值

Demo

三种类型的Trait,使用案例如下所示,详情参考注释:

// 该函数将闭包作为参数并调用它。
fn apply<F>(f: F) where
    // 闭包没有输入值和返回值。
    F: FnOnce() {
    // ^ 试一试:将 `FnOnce` 换成 `Fn` 或 `FnMut`。

    f();
}

// 输入闭包,返回一个 `i32` 整型的函数。
fn apply_to_3<F>(f: F) -> i32 where
    // 闭包处理一个 `i32` 整型并返回一个 `i32` 整型。
    F: Fn(i32) -> i32 {

    f(3)
}

fn main() {
    use std::mem;

    let greeting = "hello";
    // 不可复制的类型。
    // `to_owned` 从借用的数据创建有所有权的数据。
    let mut farewell = "goodbye".to_owned();

    // 捕获 2 个变量:通过引用捕获 `greeting`,通过值捕获 `farewell`。
    let diary = || {
        // `greeting` 通过引用捕获,只读不写,因此只需要Fn Trait。
        println!("I said {}.", greeting);

        // 下文改变了 `farewell` ,需要写,因而要求闭包通过可变引用来捕获它。
        // 因此需要 `FnMut`。
        farewell.push_str("!!!");
        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzz");

        // 手动调用 drop,需要销毁对应的内存值,又要求闭包通过值获取 `farewell`。
        // 因此现在需要 `FnOnce`。
        mem::drop(farewell);
    };

    // 以闭包作为参数,调用函数 `apply`。
    apply(diary);

    // 闭包 `double` 满足 `apply_to_3` 的 trait 约束。
    let double = |x| 2 * x;

    println!("3 doubled: {}", apply_to_3(double));
}

Example 实际例子

以下是使用Rust实现Express的回调函数式的路由添加方式。

use std::collections::HashMap;

struct Router<'a, F>
where
    F: Fn()
{
    route_map: HashMap<&'a str, F>
}

impl<'a, F> Router<'a, F> 
where
    F: Fn()
{

    #[allow(dead_code)]
    fn new() -> Self{
        Router {
            route_map: HashMap::new()
        }
    }

    #[allow(dead_code)]
    fn add_route (&mut self, path: &'a str, func: F) {
        self.route_map.insert(path, func);
    }

    fn route (&self, path: &str) {
        let route_function = self.route_map.get(path).unwrap();
        route_function();
    }
}

fn main () {
    let mut router = Router::new();
    router.add_route("home", || {
        println!("This is function for /home!");
    });
    router.route("home");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值