首页 > 其他分享 >Rust闭包

Rust闭包

时间:2022-11-27 19:56:21浏览次数:41  
标签:闭包 变量 捕获 Rust apply Fn

很多语言中都有闭包的概念,闭包就是一个能够捕获周围作用域中变量的函数,它们通常以简洁的形式展现,比如lambda表达式。

Rust的Lambda表达式

Rust中的闭包也是lambda表达式形式的,先来说一下Rust中lambda的基本格式:

|参数列表| -> 返回值 {
    语句1;
    语句2;
    语句3;
    ...
    表达式  // 最后一个表达式作为返回值
}
  1. 在lambda只有一句时,括号可以省略
  2. 返回值可以省略,由编译器自动推测
  3. 参数类型可以省略,由编译器自动推测

下面是一个返回一个数的二倍的lambda表达式:

|x| x * 2

闭包的三种变量捕获方式

上面那个例子,严格意义上来说不能算闭包,因为它没有捕获周围作用域的变量,它只用到了外部传入的参数x

在Rust中,所有变量被所有权体系管控,即使被闭包捕获,它们也必须遵从这套体系,所以,根据捕获变量方式的不同衍生出了三种闭包。

不可变借用(Fn)

let x = String::from("a variable");
let print = || println!("{}", x);

上面的闭包print中引用了周围的变量x,由于它并没有修改x,所以,实际是x的不可变引用被借用给了闭包。这种闭包在Rust中的类型为Fn

你不能在捕获了变量x的Fn类型闭包的最后一次使用之前创建变量x的可变引用或修改x的值,这是所有权系统的限制

可变借用(FnMut)

let mut x = String::from("a variable");
let mut push = || {
    x.push_str("123456");
};

上面的闭包push中修改了x,所以,x的可变引用被借用给了闭包,同时,由于闭包每次调用的内部状态也发生了改变,你必须把push也声明成mut。这种闭包的在Rust中的类型为FnMut

你不能在捕获了变量x的Fn类型闭包的最后一次使用之前创建变量x的任何引用或访问x的值,这是所有权系统的限制

获取所有权(FnOnce)

let movable = Box::new(3);

let consume = || {
    println!("movable : {:?}", movable);
    mem::drop(movable);
};

上面的闭包consume中,由于mem::drop函数需要获取参数的所有权,所以,movable被移动到闭包中,它的所有权也归闭包所有,闭包再把它移动给mem::drop

所以,在调用consume之后,无法再次调用,因为movable的所有权已经不在了。这种闭包在Rust中的类型为FnOnce

编写类型说明

上面我们说了,Rust中有三种类型的闭包,FnFnMutFnOnce,但是我们从没编写过这些类型声明,因为在上面的场景中编写类型说明其实也没啥大用。

但在将闭包作为其它函数的参数时,我们必须为其编写类型说明:

fn apply<F>(f: F) where F: Fn() {
    f();
}

apply(|| println!("I am a closure"));

Rust的trait不像Java的接口,可以直接作为多态类型来使用,Rust只能通过泛型的方式来描述一个参数具有某个trait

你也可以将闭包定义成FnOnce,但它并不表示该闭包一定必须要获取外部变量的所有权:

fn apply<F>(f: F) where F: FnOnce() {
    f();
}

// 实际上该闭包什么也没获取
apply(|| println!("I am a closure"));

简单来说,闭包的类型描述了它捕获外部变量能力的上界,如类型为FnOnce的闭包,其内部可以通过&T(不可变借用)、&mut T(可变借用)和T(移动所有权)的方式来捕获外部变量,而FnMut则不能通过T(移动所有权)的方式捕获外部变量。

fn apply<F>(f: F) where F: Fn() {
    f();
}

// x是一个外部变量,这里会出错,因为apply中定义的闭包是`Fn`类型,这里却尝试在闭包中获取外部变量的所有权
apply(|| mem::drop(x));

函数作为参数

普通函数也可以作为参数,但与闭包不同的是,它不可以捕获外部变量。

fn apply<F>(f: F) where F: Fn() {
    f();
}

fn function() {
    println!("I am closure");
    // println!("{}", &x); 错误的代码,因为函数不能捕获外部变量
}

apply(function);

闭包作为返回值

Rust的返回值必须是具体类型,不能是泛型,所以,可以这样返回闭包:

fn return_closure() -> impl Fn() {
    || println!("Hi, i returned a closure to you!")
}
return_closure()();

同理,也应该可以这样使用闭包作为参数:

fn receive_closure(f: impl Fn()) {
    f();
}
receive_closure(||println!("{}", &x));

标签:闭包,变量,捕获,Rust,apply,Fn
From: https://www.cnblogs.com/lilpig/p/16930459.html

相关文章

  • Rust的模块化概念与可见性修饰符
    【模块化编程】是指将计算机程序的功能分离成独立的、可相互作用的“模块”的软件设计概念,每个模块都包含着执行一个预期功能的代码,复杂的系统被分割为小块的独立代码块。......
  • 10:生成器 迭代器 装饰器 闭包
    一:生成器#生成器:为了节约内存,拿到内存地址一边循环一边计算a=(x*2forxinrange(1,8))print(a)print(next(a))print(next(a))<generatorobject<genexpr>at0x00934......
  • 闭包以及内存泄露
    什么是闭包闭包是一种函数结构的统称,就是一群长相结构大差不差的函数群体就叫做闭包。那么它有啥特点呢?最大的特点就是儿子函数使用或者访问了祖宗函数的私有变量,这样的玩......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(1)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chpater1.GameofLife:TheBasics编写一个简单的游戏,首先是......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(2)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chapter2ExtendedLifeUNDERSTANDINGOWNERSHIP首先需要解释R......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(4)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chapter4Hangman虽然我们不会做任何图形界面,但我们仍然可以做......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(3)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chapter3BuildingaLibraryREFERENCES在我们将一个多维数组......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(6)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chapter6ClientsandServers这章程序的一个大纲RUSTTCPSE......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(5)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chapter5InConcurrenceMutexesandSemaphoresusestd::sync......
  • 《BEGINNING RUST PROGRAMMING》---读书随记(7)
    BEGINNINGRUSTPROGRAMMINGAuthor:RicMessier如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的Chapter8GoingRelational关于不同类型的数据库平台在Rust中......