mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2026-08-19 04:33:27 +08:00
Updated 'src/advanced_features/adv_fns_and_closures.md'.
This commit is contained in:
6
projects/fn_pointer/Cargo.toml
Normal file
6
projects/fn_pointer/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "fn_pointer"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
15
projects/fn_pointer/src/main.rs
Normal file
15
projects/fn_pointer/src/main.rs
Normal 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:?}");
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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#枚举取值) 小节,回顾咱们所定义的各个枚举变种名字,也会成为一个初始化函数。咱们可以将这些初始化函数,作为实现了那些闭包特质的函数指针使用,这就意味着咱们可以把这些初始化函数,指定为取闭包的方法的参数,像下面这样:
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
# 关于宏
|
||||
|
||||
**Macros**
|
||||
|
||||
# 宏,macro
|
||||
|
||||
贯穿这本书,咱们业已用到像是 `println!` 这样的宏,但咱们并未完整地探讨过何为宏,以及其工作原理。 *宏,macro* 这个术语,指的是 Rust 中的一个特性家族:有着 `macro_rules!` 的 *声明式,declarative* 宏,与如下三种 *程序性,procedural* 宏:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user