Updated 'src/advanced_features/adv_fns_and_closures.md'.

This commit is contained in:
Hector PENG
2026-04-25 07:19:22 +08:00
parent c3bd9a87a8
commit 6f70dad6cc
6 changed files with 44 additions and 23 deletions

View File

@@ -0,0 +1,6 @@
[package]
name = "fn_pointer"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@@ -0,0 +1,15 @@
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let list_of_numbers = vec! [1, 2, 3];
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(|i| i.to_string()).collect();
println! ("{list_of_strings:?}");
}

View File

@@ -129,7 +129,7 @@
- [高级特质](advanced_features/adv_traits.md)
- [高级类型](advanced_features/adv_types.md)
- [高级函数与闭包](advanced_features/adv_fns_and_closures.md)
- [关于](advanced_features/macros.md)
- [macro](advanced_features/macros.md)
- [最后项目:构建多线程的 Web 服务器](Ch21_Final_Project_Building_a_Multithreaded_Web_Server.md)
- [构建单线程的 Web 服务器](final_project/single-threaded.md)

View File

@@ -5,11 +5,11 @@
## 函数指针
们已讲到了怎样把闭包传递给函数;咱们也可以常规函数传递给函数!咱们打算传递一个咱们已定义的函数,而非定义出一个新闭包时,这种技巧便是有用的。这些函数会强制转换到类型 `fn` 有着小写的 `f`),而不会与那个 `Fn` 闭包特质混淆。这个 `fn` 类型被称为 *函数指针funciton pointer*使用函数指针传递函数,将实现把函数作为其他函数参数而运用。
指明某个函数是个函数指针的语法,与参数是个闭包的语法类似,如下清单 19-27 中所示,其中咱们定义了一个往其参数加一的函数 `add_one`。函数 `do_twice` 则会取两个参数:到任何的取一个 `i32` 参数,并返回 `i32` 值函数的函数指针,以及一个 `i32` 值。这个 `do_twice` 函数会调用函数 `f` 两次,传递给他那个 `arg` 值,随后把这两次函数调用的结果相加在一起。`main` 函数使用了参数 `add_one``5` 调用 `do_twice`
们已经讨论了怎样传递闭包给函数;咱们也可以传递常规函数给函数!咱们打算传递某个已定义的函数,而不是定义新的闭包时,这种技巧非常有用。函数会强制转换 `fn` 类型`f` 小写),而不会与 `Fn` 闭包特质混淆。`fn` 类型被称为 *函数指针funciton pointer*通过函数指针传递函数将允许咱们把函数作为其他函数参数使用。
指定参数为函数指针的语法与闭包的语法类似,如下清单 20-28 中所示,其中我们定义了个名为 `add_one` 的函数,会将其参数加 1。函数 `do_twice` 取两个参数:一个指向任何取 `i32` 的参数并返回 `i32` 值的函数的函数指针,以及一个 `i32` 值。`do_twice` 函数调用函数 `f` 两次,向其传递 `arg` 值,然后将两次函数调用的结果相加。`main` 函数以参数 `add_one``5` 调用 `do_twice`
<a name="listing_20-28"></a>
文件名:`src/main.rs`
```rust
@@ -21,46 +21,48 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println! ("答案为:{}", answer);
println! ("答案为:{answer}");
}
```
**清单 20-28**:使用 `fn` 类型接受作为参数的函数指针
*清单 19-27使用 `fn` 类型来以参数方式接收函数指针*
这段代码打印 `答案为12`。我们指定 `do_twice` 中的参数 `f` 是个 `fn`,取 `i32` 类型的参数并返回一个 `i32`。然后我们可以在 `do_twice` 的函数体中调用 `f`。在 `main` 中,我们可以作为第一个参数传递函数名 `add_one``do_twice`
与闭包不同,`fn` 是一种类型而非特质,因此我们可以直接指定 `fn` 为参数类型,而不是以 `Fn` 的特质之一作为特质边界,声明一个泛型类型参数。
此代码会打印出 `答案为12`。咱们指明了 `do_twice` 中的参数 `f` 是取一个类型 `i32` 参数,并返回一个 `i32` `fn`。最后咱们便可以在 `do_twice` 函数体中调用 `f` 了。在 `main` 中,咱们可以将名为 `add_one` 的函数,作为首个参数传递给 `do_twice`
函数指针实现了所有三个闭包特质(`Fn``FnMut` `FnOnce`),这意味着咱们始终可以作为参数,传递函数指针给期望闭包的函数。最好使用泛型类型和闭包特质之一编写函数,以便咱们的函数既可以接受函数,也可以接受闭包
也就是说,咱们只希望接受 `fn` 而不接受闭包的一种示例是与不支持闭包的外部代码交互时C 函数可以接受函数作为参数,但 C 不支持闭包。
与闭包不同,`fn` 是种类型而非一个特质,因此咱们将 `fn` 直接指定为参数类型,而非使用 `Fn` 特质之一,作为特质边界声明一个泛型参数
函数指针实现了全部三个闭包特质(`Fn``FnMut``FnOnce`),意味着咱们可以一直将某个函数,作为期望得到一个闭包的函数的参数而加以传递。编写出使用了一个泛型及闭包特质之一的函数,是最佳做法,如此咱们的函数就既可以接收函数,也可以接收闭包了。
那就是说,一种咱们只想接收 `fn` 而不想接收闭包的情况便是与并无闭包的外部代码相交互时C 语言函数可以参数方式接收函数,但 C 语言是没有闭包的。
而作为既可以使用内联定义的闭包,又可以使用命名函数的一种情况,下面就来看看标准库中 `Iterator` 特质所提供的 `map` 函数的一种用法。要使用 `map` 函数来将某个一些数字构成的矢量值,转换为字符串的矢量,咱们可以使用一个闭包,如下面这样:
作为咱们既可以使用内联定义的闭包,也可以使用命名函数的示例,我们来看看标准库中 `Iterator` 特质提供的 `map` 方法的用法。要使用 `map` 方法将一个数字矢量转换为字符串矢量,我们可以使用闭包,如下清单 20-29 中所示
<a name="listing_20-29"></a>
```rust
let list_of_numbers = vec! [1, 2, 3];
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(|i| i.to_string()).collect();
```
或者咱们可以命名一个作为给 `map` 参数的函数,而非那个闭包,如下面这样:
**清单 20-29**:对 `map` 方法使用闭包,以转换数字为字符串
或者,我们可以将一个函数作为 `map` 的参数代替闭包。下面清单 20-30 展示了这种做法的样子。
<a name="listing_20-30"></a>
```rust
let list_of_numbers = vec! [1, 2, 3];
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(ToString::to_string).collect();
```
请注意由于有着多个可用的名为 `to_string` 函数,因此咱们就必须使用早先在 [“高级特质”](#高级特质) 小节中讲到的完全合格语法。这里咱们使用了那个标准库已对任何实现了 `Display` 类型,实现过了的 `ToString` 特质中的 `to_string` 函数
**清单 20-30**:对 `map` 方法使用 `String::to_string` 函数,以转换数字为字符串
请注意,由于存在名为 `to_string` 的可用函数,因此我们必须使用在 [高级特质](./adv_traits.md#fq_syntax) 小节中提到的完全限定语法。
在这里, 我们使用的是定义在 `ToString` 特质中的 `to_string` 函数,标准库已针对任何实现 `Display` 特质的类型实现了这一特质。
自第 6 章 [“枚举取值”](Ch06_Enums_and_Pattern_Matching.md#枚举取值) 小节,回顾咱们所定义的各个枚举变种名字,也会成为一个初始化函数。咱们可以将这些初始化函数,作为实现了那些闭包特质的函数指针使用,这就意味着咱们可以把这些初始化函数,指定为取闭包的方法的参数,像下面这样:

View File

@@ -230,6 +230,7 @@ $ cargo run
由于 `fly` 方法取一个 `self` 参数,因此当我们有两种都实现了同一个 *特质**类型*Rust 可以根据 `self` 的类型来确定要使用哪个特质的实现。
<a name="fq_syntax"></a>
然而,不是方法的关联函数没有 `self` 参数。当存在多个类型或特质,以相同的函数名字定义了非方法的函数时,除非咱们使用 *完全限定语法fully qualified syntax*,否则 Rust 并不总是知道咱们所指的是何种类型。例如,在下面清单 20-20 中,我们为动物收容所创建了一个特质,他们打算将所有狗崽取名为点点。我们以一个关联的非方法函数,构造了一个 `Animal` 特质。`Animal` 特质针对结构体 `Dog` 予以实现,我们还在 `Dog` 上直接提供了一个关联的非方法函数 `baby_name`
<a name="listing_20-20"></a>

View File

@@ -1,7 +1,4 @@
# 关于
**Macros**
# 宏macro
贯穿这本书,咱们业已用到像是 `println!` 这样的宏,但咱们并未完整地探讨过何为宏,以及其工作原理。 *macro* 这个术语,指的是 Rust 中的一个特性家族:有着 `macro_rules!`*声明式declarative* 宏,与如下三种 *程序性procedural* 宏: