10.1 提取函数以消除重复代码
10.1.1. 重复代码
看个例子:
fn main(){
let number_list = vec![1,2,3,4,5];
let mut largest = number_list[0];
for &item in number_list.iter(){
if item > largest{
largest = item;
}
}
println!("The largest number is {}", largest);
}
这个程序的目的在于寻找Vector里最大的数值。其逻辑很好理解,把第一个元素提取出来作为临时的最大值,再使用循环遍历比较Vector里的所有元素,如果当前元素大于最大值存储的值,那就把当前元素的值赋给最大值。
输出:
The largest number is 5
如果这个时候又新增加了一个需求,需要在另外一个Vector里挑出最大值,仍然可以按照刚才的逻辑写:
fn main(){
let number_list = vec![1,2,3,4,5];
let mut largest = number_list[0];
for &item in number_list.iter(){
if item > largest{
largest = item;
}
}
println!("The largest number is {}", largest);
let number_list = vec![6,7,8,9,10];
let mut largest = number_list[0];
for &item in number_list.iter(){
if item > largest{
largest = item;
}
}
println!("The largest number is {}", largest);
}
但是可以看出,这么写重复的代码太多了。
重复的代码容易出错,一旦我们需要修改逻辑就需要在多处进行修改。
所以非常推荐通过定义函数的方法来创建抽象,定义函数。代码如下:
fn largest(list: &[i32]) -> i32{
let mut largest = list[0];
for &item in list.iter(){
if item > largest{
largest = item;
}
}
largest
}
fn main(){
let number_list = vec![1,2,3,4,5];
let largest_num = largest(&number_list);
println!("The largest number is {}", largest_num);
let number_list = vec![6,7,8,9,10];
let largest_num = largest(&number_list);
println!("The largest number is {}", largest_num);
}
声明了叫largest的函数,它传入的参数是元素类型为i32的切片,然后返回值是i32。函数体内的逻辑与上文无异。需要注意的是其参数&[i32]是切片,实际上就是引用(切片的具体介绍在4.5. 切片(Slice),这里不做赘述)。
这个函数在保持逻辑不变的情况下还可以这么写:
#![allow(unused)]
fn main() {
fn largest(list: &[i32]) -> i32{
let mut largest = list[0];
for &item in list{
if item > largest{
largest = item;
}
}
largest
}
}
对比上文主要是去掉了迭代器.iter(),但不影响代码功能,因为Vector实现了IntoIterator,所以它会隐式调用list.iter()。这两种写法的行为在语义上是完全等价的,Rust 的 for 循环会为切片自动调用 iter(),因此可以直接省略迭代器显式调用。选择哪种写法主要看代码风格和个人偏好。
还有其他写法:
#![allow(unused)]
fn main() {
fn largest(list: &[i32]) -> i32{
let mut largest = list[0];
for item in list{
if *item > largest{
largest = *item;
}
}
largest
}
}
这个写法与前两者最大的不同是显式地对item进行解引用(*item),以比较它的值。
在先前的两种代码中使用了解引用模式匹配,你可以这么理解:&item = &i32,两边同时去掉&,所以item = i32,largest是i32类型,两者类型相同可以直接比较,下文自然就不需要解引用,如果item前不加&,那么item的类型就是&i32,largest是i32类型,两者不能直接比较,所以得先解引用,也就是在item前加*。
输出:
The largest number is 5
The largest number is 10
10.1.2. 消除重复的步骤
- 识别重复代码
- 创建函数,提取重复代码到函数体中,并在函数签名中指定函数的输入和返回值
- 将重复的代码使用函数调用进行替代