diff --git a/projects/fn_pointer/Cargo.toml b/projects/fn_pointer/Cargo.toml new file mode 100644 index 0000000..97b0688 --- /dev/null +++ b/projects/fn_pointer/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "fn_pointer" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/fn_pointer/src/main.rs b/projects/fn_pointer/src/main.rs new file mode 100644 index 0000000..625a9f5 --- /dev/null +++ b/projects/fn_pointer/src/main.rs @@ -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 = + list_of_numbers.iter().map(|i| i.to_string()).collect(); + + println! ("{list_of_strings:?}"); +} diff --git a/src/SUMMARY.md b/src/SUMMARY.md index c144092..0f53116 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -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) diff --git a/src/advanced_features/adv_fns_and_closures.md b/src/advanced_features/adv_fns_and_closures.md index 86ecaf7..e46953c 100644 --- a/src/advanced_features/adv_fns_and_closures.md +++ b/src/advanced_features/adv_fns_and_closures.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`。 + 文件名:`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 中所示。 + ```rust let list_of_numbers = vec! [1, 2, 3]; let list_of_strings: Vec = list_of_numbers.iter().map(|i| i.to_string()).collect(); ``` -或者咱们可以命名一个作为给 `map` 参数的函数,而非那个闭包,如下面这样: +**清单 20-29**:对 `map` 方法使用闭包,以转换数字为字符串 +或者,我们可以将一个函数作为 `map` 的参数代替闭包。下面清单 20-30 展示了这种做法的样子。 + + ```rust let list_of_numbers = vec! [1, 2, 3]; let list_of_strings: Vec = 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#枚举取值) 小节,回顾咱们所定义的各个枚举变种名字,也会成为一个初始化函数。咱们可以将这些初始化函数,作为实现了那些闭包特质的函数指针使用,这就意味着咱们可以把这些初始化函数,指定为取闭包的方法的参数,像下面这样: diff --git a/src/advanced_features/adv_traits.md b/src/advanced_features/adv_traits.md index 023c51c..b39e57c 100644 --- a/src/advanced_features/adv_traits.md +++ b/src/advanced_features/adv_traits.md @@ -230,6 +230,7 @@ $ cargo run 由于 `fly` 方法取一个 `self` 参数,因此当我们有两种都实现了同一个 *特质* 的 *类型* 时,Rust 可以根据 `self` 的类型来确定要使用哪个特质的实现。 + 然而,不是方法的关联函数没有 `self` 参数。当存在多个类型或特质,以相同的函数名字定义了非方法的函数时,除非咱们使用 *完全限定语法,fully qualified syntax*,否则 Rust 并不总是知道咱们所指的是何种类型。例如,在下面清单 20-20 中,我们为动物收容所创建了一个特质,他们打算将所有狗崽取名为点点。我们以一个关联的非方法函数,构造了一个 `Animal` 特质。`Animal` 特质针对结构体 `Dog` 予以实现,我们还在 `Dog` 上直接提供了一个关联的非方法函数 `baby_name`。 diff --git a/src/advanced_features/macros.md b/src/advanced_features/macros.md index a832501..59f27d1 100644 --- a/src/advanced_features/macros.md +++ b/src/advanced_features/macros.md @@ -1,7 +1,4 @@ -# 关于宏 - -**Macros** - +# 宏,macro 贯穿这本书,咱们业已用到像是 `println!` 这样的宏,但咱们并未完整地探讨过何为宏,以及其工作原理。 *宏,macro* 这个术语,指的是 Rust 中的一个特性家族:有着 `macro_rules!` 的 *声明式,declarative* 宏,与如下三种 *程序性,procedural* 宏: