查看原文
其他

写给javascript程序员的rust教程(三)函数与流程控制【译】

coolder117 码农真经 2023-12-25

这是写给javascript程序员的rust教程系列文章的第三部分:函数与流程控制。前两部分请戳:

函数

Rust的函数语法与JavaScript中的语法非常相似。

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,即为隐式返回,一定要把这一行的分号去掉。以上函数可以重构为:

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的箭头函数非常相似。

无参函数

// JavaScript
let greet = () => console.log("hello");

greet(); // "hello"

vs

// Rust
let greet = || println!("hello");

greet(); // "hello"

有参函数

// JavaScript
let greet = (msg) => console.log(msg);

greet("good morning!"); // "good morning!"

vs

// Rust
let greet = |msg: &str| println!("{}", msg);

greet("good morning!"); // "good morning!"

函数有返回值

// JavaScript
let add = (a, b) => a + b;

add(1, 2); // 3

vs

// Rust
let add = |a: i32, b: i32| -> i32 { a + b };

add(1, 2); // 3

复杂函数(函数体多行)

// JavaScript
let add = (a, b) => {
let sum = a + b;
return sum;
};

add(1, 2); // 3

vs

// Rust
let add = |a: i32, b: i32| -> i32 {
let sum = a + b;
return sum;
};

add(1, 2); // 3

总结为下图:

闭包大多数时候不需要类型注释,但是为了清晰起见,我在这里添加了它们。


if else 条件判断

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

fn main() {
let mut count = 0;

while count < 10 {
println!("{}", count);
count += 1;
}
}

Rust 中没有常规的for循环,你可以使用while循环或 for … in 循环。其中,for … in 循环与JavaScript 的for .. of 循环类似,它们遍历可迭代对象。

fn main() {
let numbers = [1, 2, 3, 4, 5];

for n in numbers.iter() {
println!("{}", n);
}
}

请注意,我们不是直接在数组上进行迭代,而是使用数组的iter方法。

同时也可以遍历范围:

fn main() {
for n in 1..5 {
println!("{}", n);
}
}

迭代器

在JavaScript中,我们可以使用诸如map/filter/reduce等声明式的数组方法来代替for循环来对数组执行计算或转换。

例如,这里我们采用一个数字数组,将它们加倍并过滤出小于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,我们需要遵循以下步骤

  1. 通过iter,into_iter或iter_mut等方法,将vector对象转换成迭代器。

  2. 链式调用map/filter等迭代器方法(适配器)

  3. 最后通过collect,find,sum等“消费者”方法将可迭代对象转换成vector对象或其它类型。

迭代器、适配器、消费者简单示意图:

// Iterator Adapter Consumer
// | | |
let my_squares: Vec<_> = (1..6).map(|x| x * x).collect();
println!("{:?}", my_squares);

这是等效的Rust代码:

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<i32>

&是引用操作符,*是去引用操作符。iter方法不是复制向量中的元素,而是将它们作为引用传递给链式调用的方法。这就是为什么我们在map的闭包(double)中使用&i32。这个闭包返回i32,但filter用引用调用它的闭包(less_than_10),所以这就是为什么我们需要再次使用&i32。为了去引用参数,我们使用*操作符。我们将在以后的章节中更详细地介绍这个问题。

关于Vec<i32>,到目前为止,我们还没有给变量添加类型注释,因为Rust可以自动推断类型,但对于collect,我们需要明确告诉Rust,我们期望有Vec<i32>的输出。

除了map和filter,还有一大堆迭代方法,我们可以在迭代器中使用。

谢谢你的阅读!


欢迎关注"码中人"微信公众号获取最新内容,同时欢迎加入码农读书交流群。


继续滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存