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

[广告:最高 ¥2000 红包]阿里云服务器、主机等产品通用,可叠加官网常规优惠使用 | 限时领取

这是写给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对象。

这是等效的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,还有一大堆迭代方法,我们可以在迭代器中使用。

谢谢你的阅读!

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

码中人 微信公众号

关注微信公众号

码中人 微信公众号