锈蚀封闭的最佳解释

So far, we’ve made lots of functions in Rust, but we’ve given them all names. Rust also allows us to create anonymous functions. Rust’s anonymous functions are called closures. By themselves, closures aren’t all that interesting, but when you combine them with functions that take closures as arguments, really powerful things are possible.

到目前为止,我们在Rust中做了很多功能,但是我们给它们起了全名。 Rust还允许我们创建匿名函数。 Rust的匿名函数称为闭包。 就闭包本身而言,并不是那么有趣,但是当您将它们与将闭包作为参数的函数结合使用时,真正强大的事情才有可能实现。

Let’s make a closure:

让我们做一个封闭:

let add_one = |x| { 1 + x };println!("The sum of 5 plus 1 is {}.", add_one(5));

We create a closure using the |...| { ... } syntax, and then we create a binding so we can use it later. Note that we call the function using the binding name and two parentheses, just like we would for a named function.

我们使用|...| { ... }创建一个闭包|...| { ... }语法,然后创建绑定,以便以后使用。 注意,我们使用绑定名称和两个括号来调用函数,就像命名函数一样。

Let’s compare syntax. The two are pretty close:

让我们比较一下语法。 两者非常接近:

let add_one = |x: i32| -> i32 { 1 + x };
fn add_one (x: i32) -> i32 { 1 + x }

As you may have noticed, closures infer their argument and return types, so you don’t need to declare one. This is different from named functions, which default to returning unit (()).

您可能已经注意到,闭包会推断出它们的参数和返回类型,因此您无需声明一个。 这与命名函数不同,命名函数默认为返回单位( () )。

There’s one big difference between a closure and named functions, and it’s in the name: a closure “closes over its environment.” What does that mean? It means this:

闭包和命名函数之间有一个很大的区别,它的名字是:闭包“封闭了环境”。 这意味着什么? 这意味着:

fn main() {
let x: i32 = 5; let printer = || { println!("x is: {}", x); }; printer(); // prints "x is: 5"
}

The || syntax means this is an anonymous closure that takes no arguments. Without it, we'd just have a block of code in {}s.

|| 语法意味着这是一个不带参数的匿名闭包。 没有它,我们将只在{}有一段代码。

In other words, a closure has access to variables in the scope where it’s defined. The closure borrows any variables it uses, so this will error:

换句话说,闭包可以访问其定义范围内的变量。 闭包借用了它使用的任何变量,因此将出错:

fn main() {
let mut x: i32 = 5; let printer = || { println!("x is: {}", x); }; x = 6; // error: cannot assign to `x` because it is borrowed
}

移动封盖 (Moving closures)

Rust has a second type of closure, called a moving closure. Moving closures are indicated using the move keyword (e.g., move || x * x). The difference between a moving closure and an ordinary closure is that a moving closure always takes ownership of all variables that it uses. Ordinary closures, in contrast, just create a reference into the enclosing stack frame. Moving closures are most useful with Rust's concurrency features, and so we'll just leave it at this for now. We'll talk about them more in the "Threads" section of the guide.

Rust具有第二种类型的封闭,称为移动封闭。 移动闭包使用move关键字指示(例如, move || x * x )。 移动闭包和普通闭包之间的区别在于,移动闭包始终拥有其使用的所有变量的所有权。 相比之下,普通闭包仅在封装堆栈框架中创建引用。 移动闭包对Rust的并发功能最有用,因此我们暂时不做介绍。 我们将在指南的“线程”部分中详细讨论它们。

接受闭包作为参数 (Accepting closures as arguments)

Closures are most useful as an argument to another function. Here’s an example:

闭包作为另一个函数的参数最有用。 这是一个例子:

fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
f(x) + f(x)
}fn main() {
let square = |x: i32| { x * x }; twice(5, square); // evaluates to 50
}

Let’s break the example down, starting with main:

让我们分解这个例子,从main开始:

let square = |x: i32| { x * x };

We’ve seen this before. We make a closure that takes an integer, and returns its square.

我们以前见过。 我们创建一个接受整数的闭包,并返回其平方。

twice(5, square); // evaluates to 50

This line is more interesting. Here, we call our function, twice, and we pass it two arguments: an integer, 5, and our closure, square. This is just like passing any other two variable bindings to a function, but if you've never worked with closures before, it can seem a little complex. Just think: "I'm passing two variables: one is an i32, and one is a function."

这条线更有趣。 在这里,我们twice调用函数,并将两个参数传递给它:整数5和闭包square 。 这就像将任何其他两个变量绑定传递给函数一样,但是如果您以前从未使用过闭包,这似乎有点复杂。 试想一下:“我正在传递两个变量:一个是i32,一个是函数。”

Next, let’s look at how twice is defined:

接下来,让我们看看如何定义twice

fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {

twice takes two arguments, x and f. That's why we called it with two arguments. x is an i32, we've done that a ton of times. f is a function, though, and that function takes an i32and returns an i32. This is what the requirement Fn(i32) -> i32 for the type parameter F says. Now F represents any function that takes an i32 and returns an i32.

twice接受两个参数xf 。 这就是为什么我们用两个参数来称呼它。 xi32 ,我们已经做了很多次了。 f是一个函数,但是该函数接受一个i32并返回一个i32 。 这就是类型参数F的要求Fn(i32) -> i32 。 现在F代表任何接受i32并返回i32

This is the most complicated function signature we’ve seen yet! Give it a read a few times until you can see how it works. It takes a teeny bit of practice, and then it’s easy. The good news is that this kind of passing a closure around can be very efficient. With all the type information available at compile-time the compiler can do wonders.

这是我们所见过的最复杂的函数签名! 仔细阅读几次,直到您了解其工作原理。 这需要一些练习,然后很容易。 好消息是,这种传递闭包可能非常有效。 有了在编译时可用的所有类型信息,编译器就可以创造奇迹。

Finally, twice returns an i32 as well.

最后, twice返回一个i32

Okay, let’s look at the body of twice:

好吧,让我们看一下twice的主体:

fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
f(x) + f(x)
}

Since our closure is named f, we can call it just like we called our closures before, and we pass in our x argument to each one, hence the name twice.

由于闭包的名称为f ,因此可以像之前调用闭包一样来命名它,并且将x参数传递给每个闭包,因此命名为twice

If you do the math, (5 * 5) + (5 * 5) == 50, so that's the output we get.

如果进行数学运算,则(5 * 5) + (5 * 5) == 50 ,这就是我们得到的输出。

Play around with this concept until you’re comfortable with it. Rust’s standard library uses lots of closures where appropriate, so you’ll be using this technique a lot.

尝试使用此概念,直到对它感到满意为止。 Rust的标准库在适当的地方使用了很多闭包,因此您将大量使用这种技术。

If we didn’t want to give square a name, we could just define it inline. This example is the same as the previous one:

如果我们不想给square一个名字,我们可以内联定义它。 此示例与上一个示例相同:

fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
f(x) + f(x)
}fn main() {
twice(5, |x: i32| { x * x }); // evaluates to 50
}

A named function’s name can be used wherever you’d use a closure. Another way of writing the previous example:

命名函数的名称可以在任何使用闭包的地方使用。 编写上一个示例的另一种方式:

fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
f(x) + f(x)
}fn square(x: i32) -> i32 { x * x }fn main() {
twice(5, square); // evaluates to 50
}

Doing this is not particularly common, but it’s useful every once in a while.

这样做并非特别常见,但偶尔会很有用。

Before we move on, let us look at a function that accepts two closures.

在继续之前,让我们看一个接受两个闭包的函数。

fn compose<F, G>(x: i32, f: F, g: G) -> i32
where F: Fn(i32) -> i32, G: Fn(i32) -> i32 {
g(f(x))
}fn main() {
compose(5,
|n: i32| { n + 42 },
|n: i32| { n * 2 }); // evaluates to 94
}

You might ask yourself: why do we need to introduce two type parameters F and G here? Evidently, both f and g have the same signature: Fn(i32) -> i32.

您可能会问自己:为什么我们在这里需要引入两个类型参数FG ? 显然, fg具有相同的签名: Fn(i32) -> i32

That is because in Rust each closure has its own unique type. So, not only do closures with different signatures have different types, but different closures with the same signature have different types, as well!

这是因为在Rust中,每个闭包都有其自己的唯一类型。 因此,不仅具有不同签名的闭包具有不同的类型,而且具有相同签名的不同闭包也具有不同的类型!

You can think of it this way: the behavior of a closure is part of its type. Therefore, using a single type parameter for both closures will accept the first of them, rejecting the second. The distinct type of the second closure does not allow it to be represented by the same type parameter as that of the first. We acknowledge this, and use two different type parameters Fand G.

您可以这样想:闭包的行为是其类型的一部分。 因此,对两个闭包使用单个类型参数将接受第一个闭包,而拒绝第二个闭包。 第二个闭包的不同类型不允许用与第一个闭包相同的类型参数表示。 我们承认这一点,并使用两个不同的类型参数FG

This also introduces the where clause, which lets us describe type parameters in a more flexible manner.

这也引入了where子句,该子句使我们可以更灵活地描述类型参数。

That’s all you need to get the hang of closures! Closures are a little bit strange at first, but once you’re used to them, you’ll miss them in other languages. Passing functions to other functions is incredibly powerful, as you will see in the following chapter about iterators.

这就是解开闭包的全部! 起初,闭包有点奇怪,但是一旦习惯了它们,就会以其他语言错过它们。 将函数传递给其他函数的功能异常强大,您将在下一章关于迭代器的文章中看到。

Reference: This article originally published in https://www.cs.brandeis.edu/~cs146a/rust/doc-02-21-2015/book/closures.html

参考:本文最初发表于https://www.cs.brandeis.edu/~cs146a/rust/doc-02-21-2015/book/closures.html

翻译自: https://medium.com/coding-rust/best-explanation-of-closure-in-rust-2b20210eba53

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值