04-函数式编程
分割线构成的框中内容摘编自:快速入门:Rust中有哪些你不得不了解的基础语法?(著作权归AI悦创所有)
Rust 中的函数和模块 (各种代码块)
最后我们来看 Rust 的函数、闭包和模块,它们用于封装和复用代码。
a. 函数
函数基本上是所有编程语言的标配,在 Rust 中也不例外,它是一种基本的代码复用方法。在 Rust 中使用 fn 关键字来定义一个函数。比如:
fn print_a_b(a: i32, b: char) {
println!("The value of a b is: {a}{b}");
}
fn main() {
print_a_b(5, 'h');
}
函数定义时的参数叫作形式参数(形参),函数调用时传入的参数值叫做实际参数(实参)。函数的调用要与函数的签名(函数名、参数个数、参数类型、参数顺序、返回类型)一致,也就是实参和形参要匹配。
函数对于几乎所有语言都非常重要,实际上各种编程语言在实现时,都是以函数作为基本单元来组织栈上的内存分配和回收的,这个基本的内存单元就是所谓的栈帧(frame),我们在下节课会讲到。
b. 闭包(Closure)
闭包是另一种风格的函数。它使用两个竖线符号 ||
定义,而不是用 fn()
来定义。
Code1
// 标准的函数定义
fn add_one_v1 (x: u32) -> u32 { x + 1 }
// 闭包的定义,请注意形式对比
let add_one_v2 = |x: u32| -> u32 { x + 1 };
// 闭包的定义2,省略了类型标注
let add_one_v3 = |x| { x + 1 };
// 闭包的定义3,花括号也省略了
let add_one_v4 = |x| x + 1 ;
Code2
fn main() {
let add_one = |x| x + 1;
let a_vec: Vec<u32> = vec![1,2,3,4,5];
let vec2: Vec<_> = a_vec.iter().map(add_one).collect();
}
闭包与函数的一个显著不同就是,闭包可以捕获函数中的局部变量,而函数不行。比如,下面示例中的闭包 add_v2
捕获了 main 函数中的局部变量 a 来使用,但是函数 add_v1
就没有这个能力。
fn main() {
let a = 10u32; // 局部变量
fn add_v1 (x: u32) -> u32 { x + a } // 定义一个内部函数
let add_v2 = |x| x + a; // 定义一个闭包
let result1 = add_v1(20); // 调用函数
let result2 = add_v2(20); // 调用闭包
println!("{}", result2);
}
这样会编译出错,并提示错误。
error[E0434]: can't capture dynamic environment in a fn item
--> src/main.rs:4:40
|
4 | fn add_v1 (x: u32) -> u32 { x + a } // 定义一个内部函数
| ^
|
= help: use the `|| { ... }` closure form instead
闭包之所以能够省略类型参数等信息,主要是其定义在某个函数体内部,从闭包的内容和上下文环境中能够分析出来那些类型信息。
c. 模块
我们不可能把所有代码都写在一个文件里面。代码量多了后,分成不同的文件模块书写是非常自然的事情。这个需求需要从编程语言层级去做一定的支持才行,Rust 也提供了相应的方案。
分文件和目录组织代码理解起来其实很简单,主要的知识点在于目录的组织结构上。比如下面示例:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden // 子目录
│ └── vegetables.rs
├── garden.rs // 与子目录同名的.rs文件,表示这个模块的入口
└── main.rs
第五行代码到第七行代码组成 garden 模块,在 garden.rs
中,使用 mod vegetables;
导入 vegetables 子模块。
在 main.rs
中,用同样的方式导入 garden 模块。
mod garden;
整个代码结构就这样一层一层地组织起来了。
另一种文件的组织形式来自 2015 版,也很常见,有很多人喜欢用。
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden // 子目录
│ └── mod.rs // 子目录中有一个固定文件名 mod.rs,表示这个模块的入口
│ └── vegetables.rs
└── main.rs
同上,由第五行到第七行代码组成 garden 模块,在 main.rs
中导入它使用。
你可以在本地创建文件,来体会两种不同目录组织形式的区别。