x-i18n: generated_at: “2026-03-01T15:01:52Z” model: gemini-3-flash-preview provider: google-gemini-cli source_hash: cfbbca957a9a1657c75e2d61a511643b06ffffbbb57da101ab2da6eccca97bf4 source_path: ch20-04-advanced-functions-and-closures.md workflow: 16
高级函数与闭包 (Advanced Functions and Closures)
本节探讨一些与函数和闭包相关的高级特性,包括函数指针和返回闭包。
This section explores some advanced features related to functions and closures, including function pointers and returning closures.
函数指针 (Function Pointers)
我们讨论过如何向函数传递闭包;你也可以向函数传递常规函数!当你想要传递一个已经定义的函数而不是定义一个新的闭包时,这种技术很有用。函数强制转换为类型 fn (带有小写字母 f),不要与 Fn 闭包特征混淆。 fn 类型被称为“函数指针 (function pointer)”。通过函数指针传递函数将允许你将函数作为其他函数的参数。
We’ve talked about how to pass closures to functions; you can also pass regular
functions to functions! This technique is useful when you want to pass a
function you’ve already defined rather than defining a new closure. Functions
coerce to the type fn (with a lowercase f), not to be confused with the
Fn closure trait. The fn type is called a function pointer. Passing
functions with function pointers will allow you to use functions as arguments
to other functions.
指定参数为函数指针的语法与闭包类似,如示例 20-28 所示,我们定义了一个在其参数上加 1 的函数 add_one 。函数 do_twice 接收两个参数:一个指向任何接收 i32 参数并返回 i32 的函数的函数指针,以及一个 i32 值。 do_twice 函数调用函数 f 两次,将 arg 值传递给它,然后将两次函数调用的结果相加。 main 函数使用参数 add_one 和 5 调用 do_twice 。
The syntax for specifying that a parameter is a function pointer is similar to
that of closures, as shown in Listing 20-28, where we’ve defined a function
add_one that adds 1 to its parameter. The function do_twice takes two
parameters: a function pointer to any function that takes an i32 parameter
and returns an i32, and one i32 value. The do_twice function calls the
function f twice, passing it the arg value, then adds the two function call
results together. The main function calls do_twice with the arguments
add_one and 5.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-28/src/main.rs}}
}
这段代码打印 The answer is: 12 。我们指定 do_twice 中的参数 f 是一个 fn ,它接收一个 i32 类型的参数并返回一个 i32 。然后我们可以在 do_twice 的主体中调用 f 。在 main 中,我们可以将函数名 add_one 作为第一个参数传递给 do_twice 。
This code prints The answer is: 12. We specify that the parameter f in
do_twice is an fn that takes one parameter of type i32 and returns an
i32. We can then call f in the body of do_twice. In main, we can pass
the function name add_one as the first argument to do_twice.
与闭包不同, fn 是一个类型而不是一个特征,所以我们直接指定 fn 作为参数类型,而不是声明一个以 Fn 特征之一作为特征约束的泛型类型参数。
Unlike closures, fn is a type rather than a trait, so we specify fn as the
parameter type directly rather than declaring a generic type parameter with one
of the Fn traits as a trait bound.
函数指针实现了所有三种闭包特征( Fn 、 FnMut 和 FnOnce ),这意味着你始终可以将函数指针作为参数传递给期望闭包的函数。最好使用泛型类型和其中一种闭包特征编写函数,以便你的函数可以接收函数或闭包。
Function pointers implement all three of the closure traits (Fn, FnMut, and
FnOnce), meaning you can always pass a function pointer as an argument for a
function that expects a closure. It’s best to write functions using a generic
type and one of the closure traits so that your functions can accept either
functions or closures.
即便如此,一个你只想接收 fn 而不接收闭包的例子是与没有闭包的外部代码交互时:C 函数可以接收函数作为参数,但 C 没有闭包。
That said, one example of where you would want to only accept fn and not
closures is when interfacing with external code that doesn’t have closures: C
functions can accept functions as arguments, but C doesn’t have closures.
作为一个既可以使用内联定义的闭包也可以使用命名函数的例子,让我们看看标准库中 Iterator 特征提供的 map 方法的一种用法。要使用 map 方法将一个数字向量转换为字符串向量,我们可以使用闭包,如示例 20-29 所示。
As an example of where you could use either a closure defined inline or a named
function, let’s look at a use of the map method provided by the Iterator
trait in the standard library. To use the map method to turn a vector of
numbers into a vector of strings, we could use a closure, as in Listing 20-29.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-29/src/main.rs:here}}
}
或者我们可以将一个函数命名为 map 的参数,而不是使用闭包。示例 20-30 展示了这看起来是什么样子的。
Or we could name a function as the argument to map instead of the closure.
Listing 20-30 shows what this would look like.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-30/src/main.rs:here}}
}
注意我们必须使用在“高级特征”一节中讨论过的完全限定语法,因为有多个名为 to_string 的可用函数。
Note that we must use the fully qualified syntax that we talked about in the
“Advanced Traits” section because there are
multiple functions available named to_string.
这里,我们使用的是 ToString 特征中定义的 to_string 函数,标准库已经为任何实现了 Display 的类型实现了该特征。
Here, we’re using the to_string function defined in the ToString trait,
which the standard library has implemented for any type that implements
Display.
回想第 6 章“枚举值”一节,我们定义的每个枚举变体的名称也成为了一个初始化函数。我们可以将这些初始化函数作为实现了闭包特征的函数指针来使用,这意味着我们可以将初始化函数指定为接收闭包的方法的参数,如示例 20-31 所示。
Recall from the “Enum Values” section in Chapter 6 that the name of each enum variant that we define also becomes an initializer function. We can use these initializer functions as function pointers that implement the closure traits, which means we can specify the initializer functions as arguments for methods that take closures, as seen in Listing 20-31.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-31/src/main.rs:here}}
}
这里,我们通过使用 Status::Value 的初始化函数,为调用 map 的范围内的每个 u32 值创建 Status::Value 实例。有些人更喜欢这种风格,有些人则更喜欢使用闭包。它们会编译成相同的代码,所以请使用对你来说更清晰的风格。
Here, we create Status::Value instances using each u32 value in the range
that map is called on by using the initializer function of Status::Value.
Some people prefer this style and some people prefer to use closures. They
compile to the same code, so use whichever style is clearer to you.
返回闭包 (Returning Closures)
闭包由特征表示,这意味着你不能直接返回闭包。在大多数你可能想返回特征的情况下,你可以改用实现该特征的具体类型作为函数的返回值。然而,对于闭包,你通常不能这样做,因为它们没有可返回的具体类型;例如,如果闭包从其作用域中捕获了任何值,你就不被允许使用函数指针 fn 作为返回类型。
Closures are represented by traits, which means you can’t return closures
directly. In most cases where you might want to return a trait, you can instead
use the concrete type that implements the trait as the return value of the
function. However, you can’t usually do that with closures because they don’t
have a concrete type that is returnable; you’re not allowed to use the function
pointer fn as a return type if the closure captures any values from its
scope, for example.
相反,你通常会使用我们在第 10 章学到的 impl Trait 语法。你可以返回任何函数类型,使用 Fn 、 FnOnce 和 FnMut 。例如,示例 20-32 中的代码将可以正常编译。
Instead, you will normally use the impl Trait syntax we learned about in
Chapter 10. You can return any function type, using Fn, FnOnce, and FnMut.
For example, the code in Listing 20-32 will compile just fine.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-32/src/lib.rs}}
}
然而,正如我们在第 13 章“推断并标注闭包类型”一节中所指出的,每个闭包也是它自己独特的类型。如果你需要处理具有相同签名但不同实现的多个函数,你将需要为它们使用特征对象。考虑一下如果你编写像示例 20-33 所示的代码会发生什么。
However, as we noted in the “Inferring and Annotating Closure Types” section in Chapter 13, each closure is also its own distinct type. If you need to work with multiple functions that have the same signature but different implementations, you will need to use a trait object for them. Consider what happens if you write code like that shown in Listing 20-33.
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-33/src/main.rs}}
这里我们有两个函数, returns_closure 和 returns_initialized_closure ,它们都返回 impl Fn(i32) -> i32 。注意它们返回的闭包是不同的,尽管它们实现了相同的类型。如果我们尝试编译这个,Rust 会让我们知道它行不通:
Here we have two functions, returns_closure and returns_initialized_closure,
which both return impl Fn(i32) -> i32. Notice that the closures that they
return are different, even though they implement the same type. If we try to
compile this, Rust lets us know that it won’t work:
{{#include ../listings/ch20-advanced-features/listing-20-33/output.txt}}
错误消息告诉我们,每当我们返回一个 impl Trait 时,Rust 都会创建一个唯一的“不透明类型 (opaque type)”,即一个我们无法查看 Rust 为我们构造的细节的类型,我们也无法猜测 Rust 将生成的类型来自己编写。所以,即使这些函数返回了实现相同特征 Fn(i32) -> i32 的闭包,Rust 为每个函数生成的不透明类型也是不同的。(这类似于 Rust 为不同的异步块产生不同的具体类型,即使它们具有相同的输出类型,正如我们在第 17 章“ Pin 类型与 Unpin 特征”中看到的。)我们已经多次见过这个问题的解决方案:我们可以使用特征对象,如示例 20-34 所示。
The error message tells us that whenever we return an impl Trait, Rust
creates a unique opaque type, a type where we cannot see into the details of
what Rust constructs for us, nor can we guess the type Rust will generate to
write ourselves. So, even though these functions return closures that implement
the same trait, Fn(i32) -> i32, the opaque types Rust generates for each are
distinct. (This is similar to how Rust produces different concrete types for
distinct async blocks even when they have the same output type, as we saw in
“The Pin Type and the Unpin Trait” in
Chapter 17.) We have seen a solution to this problem a few times now: We can
use a trait object, as in Listing 20-34.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-34/src/main.rs:here}}
}
这段代码可以正常编译。关于特征对象的更多信息,请参阅第 18 章“使用特征对象实现不同类型间的抽象行为”一节。
This code will compile just fine. For more about trait objects, refer to the section “Using Trait Objects To Abstract over Shared Behavior” in Chapter 18.
接下来,让我们看看宏!
Next, let’s look at macros!