A closure in Rust is an anonymous function you can save in a variable or pass as an argument to another function. You can create the closure using a lightweight syntax and access variables from the scope in which it’s defined.
Here’s an example of a closure that increases a number by one:
let plus_one = |x: i32| x + 1;
let a = 5;
println!("{}", plus_one(a)); // Outputs 6
In this example, plus_one
is a closure that takes one argument x
and returns x + 1
.
Closures in Rust are similar to lambdas in other languages, but they have some specific behaviors and capabilities.
Capture Environment: Closures have the ability to capture values from the scope in which they’re defined. Here’s an example:
let num = 5;
let plus_num = |x: i32| x + num;
println!("{}", plus_num(10)); // Outputs 15
Here, plus_num
captures the value of num
from the surrounding environment.
Flexible Input Types: Unlike functions, closures don’t require you to annotate the types of the input parameters. However, you can if you want to, and sometimes doing so can increase clarity.
Three Flavors of Closures: Closures in Rust come in three forms, which differ in how they capture variables from the surrounding scope: Fn
, FnMut
, and FnOnce
. The type is chosen by the compiler based on how the closure uses variables from the environment.
FnOnce
consumes the variables it captures from its enclosing scope, known as the “once” because it can’t take ownership more than once.FnMut
can change the environment because it mutably borrows values.Fn
borrows values from the environment immutably.Let’s start with an overview of each:
FnOnce captures variables and moves them into the closure when it is defined. It can consume those variables when the closure is called. FnOnce closures can’t be called more than once in some contexts without duplication.
FnMut can mutate and also consume variables from the environment in which it is defined.
Fn borrows variables from the environment immutably.
Now, let’s have a look at some examples for each type:
// Example of FnOnce
let x = "hello".to_string();
let consume_x = move || println!("{}", x);
consume_x(); // "hello"
// consume_x(); This won't compile because `x` has been moved into the closure
// Example of FnMut
let mut y = "hello".to_string();
let mut append_world = || y.push_str(" world");
append_world();
println!("{}", y); // "hello world"
// Example of Fn
let z = "hello".to_string();
let print_z = || println!("{}", z);
print_z();
print_z(); // We can call this as many times as we want.
① In the first example, the closure takes ownership of x
(indicated by the move
keyword), so it’s a FnOnce
.
② In the second example, the closure mutably borrows y
, so it’s a FnMut
.
③ In the third example, the closure immutably borrows z
, so it’s a Fn
.
Rust chooses how to capture variables on the fly without any annotation required. The move
keyword is used to force the closure to take ownership of the values it uses.
Closures are a fundamental feature for many Rust idioms. They are used extensively in iterator adapters, threading, and many other situations.
fn main() {
/*
|| 代替 ()将输入参数括起来
函数体界定符是{},对于单个表达式是可选的,其他情况必须加上。
有能力捕获外部环境的变量。
|参数列表| {
业务逻辑
}
|| {
业务逻辑
}
闭包可以赋值一个变量。
*/
let double = |x| {x * 2};
let add = |a, b| {a + b};
let x = add(2, 4);
println!("{}", x); // 6
let y = double(5);
println!("{}", y); // 10
let v = 3;
let add2 = |x| {v + x};
println!("{}", add2(4)); // 7
/*
闭包,可以在没有标注的情况下运行。可移动(move), 可借用(borrow),
闭包可以通过
引用 &T
可变引用 &mut T
值 T
捕获
1. 闭包是一个在函数内创建立即调用的另外一个函数。
2. 闭包是一个匿名函数
3. 闭包虽然没有函数名,但可以把整个闭包赋值一个变量,
通过该变量来完成闭包的调用
4. 闭包不用声明返回值,但可以有返回值。并且使用最后一条语句的执行结果
作为返回值,闭包的返回值也可以给变量。
5. 闭包也称之为内联函数,可以访问外层函数里的变量。
*/
}
fn main() {
let add = |x, y| x + y;
let result = add(3, 4);
println!("{}", result); // 7
receives_closure(add); // // 闭包作为参数执行结果 => 8
let y = 2;
receives_closure2(|x| x + y); // closure(1) => 3
let y = 3;
receives_closure2(|x| x + y); // closure(1) => 4
let closure = returns_closure();
println!("返回闭包 => {}", closure(1)); // 返回闭包 => 7
let result = do1(add, 5);
println!("result(1) => {}", result(1)); // result(1) => 6
let result = do2(add, 5);
println!("result(2) => {}", result(2)); // result(2) => 7
}
fn do2<F, X, Y, Z>(f: F, x: X) -> impl Fn(Y) -> Z
where
F: Fn(X, Y) -> Z,
X: Copy,
{
move |y| f(x, y)
}
// 参数和返回值都有闭包
fn do1<F>(f: F, x: i32) -> impl Fn(i32) -> i32
where
F: Fn(i32, i32) -> i32,
{
move |y| f(x, y)
}
// 返回闭包
fn returns_closure() -> impl Fn(i32) -> i32 {
|x| x + 6
}
// 闭包作为参数
fn receives_closure<F>(closure: F)
where
F: Fn(i32, i32) -> i32,
{
let result = closure(3, 5);
println!("闭包作为参数执行结果 => {}", result)
}
// 闭包捕获变量
fn receives_closure2<F>(closure: F)
where
F: Fn(i32) -> i32,
{
let result = closure(1);
println!("closure(1) => {}", result);
}