这是写给javascript程序员的rust教程系列文章的第三部分,函数与流程控制。前两部分请戳:
函数
Rust的函数语法与JavaScript中的语法非常相似。
1
2
3
4
5
6
7
8
9
|
fn main() {
let income = 100;
let tax = calculate_tax(income);
println!("{}", tax);
}
fn calculate_tax(income: i32) -> i32 {
return income * 90 / 100;
}
|
以上Rust函数与JavaScript函数的主要区别是:参数的类型声明及返回值 。
Rust 函数中的 return 关键字是可以省略的,显式返回值不是必须的。如果去掉return,即为隐式返回,一定要把这一行的分号去掉。以上函数可以重构为:
1
2
3
4
5
6
7
8
9
10
|
fn main() {
let income = 100;
let tax = calculate_tax(income);
println!("{}", tax);
}
fn calculate_tax(income: i32) -> i32 {
- return income * 90 / 100;
+ income * 90 / 100
}
|
箭头函数
箭头函数是现代JavaScript中一个很流行的功能,它们让我们能够以一种简洁的方式编写功能代码。
Rust也有类似的功能,它们被称为 “Closures”。这个名字可能会让人有点困惑,需要习惯,因为在JavaScript中,闭包可以使用普通函数和箭头函数来创建。
Rust的闭包语法与JavaScript的箭头函数非常相似。
无参函数
1
2
3
4
|
// JavaScript
let greet = () => console.log("hello");
greet(); // "hello"
|
vs
1
2
3
4
|
// Rust
let greet = || println!("hello");
greet(); // "hello"
|
有参函数
1
2
3
4
|
// JavaScript
let greet = (msg) => console.log(msg);
greet("good morning!"); // "good morning!"
|
vs
1
2
3
4
|
// Rust
let greet = |msg: &str| println!("{}", msg);
greet("good morning!"); // "good morning!"
|
函数有返回值
1
2
3
4
|
// JavaScript
let add = (a, b) => a + b;
add(1, 2); // 3
|
vs
1
2
3
4
|
// Rust
let add = |a: i32, b: i32| -> i32 { a + b };
add(1, 2); // 3
|
复杂函数(函数体多行)
1
2
3
4
5
6
7
|
// JavaScript
let add = (a, b) => {
let sum = a + b;
return sum;
};
add(1, 2); // 3
|
vs
1
2
3
4
5
6
7
|
// Rust
let add = |a: i32, b: i32| -> i32 {
let sum = a + b;
return sum;
};
add(1, 2); // 3
|
总结为下图:
闭包大多数时候不需要类型注释,但是为了清晰起见,我在这里添加了它们。
if else 条件判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
fn main() {
let income = 100;
let tax = calculate_tax(income);
println!("{}", tax);
}
fn calculate_tax(income: i32) -> i32 {
if income < 10 {
return 0;
} else if income >= 10 && income < 50 {
return 20;
} else {
return 50;
}
}
|
循环Loops
while
1
2
3
4
5
6
7
8
|
fn main() {
let mut count = 0;
while count < 10 {
println!("{}", count);
count += 1;
}
}
|
Rust 中没有常规的for循环,你可以使用while循环或 for … in 循环。其中,for … in 循环与JavaScript 的for .. of 循环类似,它们在迭代器上循环。
1
2
3
4
5
6
7
|
fn main() {
let numbers = [1, 2, 3, 4, 5];
for n in numbers.iter() {
println!("{}", n);
}
}
|
请注意,我们不是直接在数组上进行迭代,而是使用数组的iter方法。
同时也可以遍历范围:
1
2
3
4
5
|
fn main() {
for n in 1..5 {
println!("{}", n);
}
}
|
迭代器
在JavaScript中,我们可以使用诸如map/filter/reduce等声明式的数组方法来代替for循环来对数组执行计算或转换。
例如,这里我们采用一个数字数组,将它们加倍并过滤出小于10的元素:
1
2
3
4
5
6
7
8
9
10
|
function main() {
let numbers = [1, 2, 3, 4, 5];
let double = (n) => n * 2;
let less_than_ten = (n) => n < 10;
let result = numbers.map(double).filter(less_than_ten);
console.log(result); // [2, 4, 6, 8]
}
|
在Rust中,我们不能直接在vectors上使用map/filter,我们需要遵循以下步骤
- 通过iter,into_iter或iter_mut等方法,将vector对象转换成可迭代对象。
- 链式调用map/filter等迭代器方法
- 最后通过collect,find,sum等“消费者”方法将可迭代对象转换成vector对象。
这是等效的Rust代码:
1
2
3
4
5
6
7
8
9
10
|
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let double = |n: &i32| -> i32 { n * 2 };
let less_than_10 = |n: &i32| -> bool { *n < 10 };
let result: Vec<i32> = numbers.iter().map(double).filter(less_than_10).collect();
println!("{:?}", result); // [2, 4, 6, 8]
}
|
您应该能够理解上面的大多数代码,但是您可能会注意到这里的一些细节:
- 闭包中& 和 * 的用法
- 变量result的类型声明为Vec
**&是引用操作符,*是去引用操作符。**iter方法不是复制向量中的元素,而是将它们作为引用传递给链式调用的方法。这就是为什么我们在map的闭包(double)中使用&i32。这个闭包返回i32,但filter用引用调用它的闭包(less_than_10),所以这就是为什么我们需要再次使用&i32。为了去引用参数,我们使用*操作符。我们将在以后的章节中更详细地介绍这个问题。
关于Vec,到目前为止,我们还没有给变量添加类型注释,因为Rust可以自动推断类型,但对于collect,我们需要明确告诉Rust,我们期望有Vec的输出。
除了map和filter,还有一大堆迭代方法,我们可以在迭代器中使用。
谢谢你的阅读!
欢迎关注码中人微信公众号获取最新内容,同时欢迎加入码农读书交流群。