学习Rust用if, else-if, else来管理控制流

261 阅读6分钟

在本章中,我们将介绍控制流,如何让你的代码有条件地执行,以及在需要时重复执行。

简介

在本章中,你将:

  • 使用if、else和else if在你的代码中创建不同的执行路径。
  • 应用循环来重复语句。
  • 在循环中添加条件,以中断或跳过迭代。
  • 迭代数据集合。

if 创建一个新的执行路径

让我们考虑一下这组语句。

let salary = 220.2;
let other_income = 10.5;
let tax = 0.2; // percentage

let takehome_salary = (salary + other_income) * (1 as f32 - tax);

上面我们有一个关于某人工资的计算。想象一下,虽然某人的收入低于一定的收入水平,就可以免于纳税,那么怎么办?

对于这种情况,我们可以用一个if 结构来表达这个条件。

我们的代码现在可以这样表达。

let no_tax_value = 300.0;
let salary = 220.2;
let other_income = 10.5;
let tax = 0.2; // percentage

let mut takehome_salary = salary + other_income;

if (salary + other_income) > no_tax_value {
  takehome_salary = (salary + other_income) * (1 as f32 - tax); // tax deducted
}

if 子句评估了一个表达式(salary + other_income) > no_tax_value ,如果评估为true ,将运行括号内的代码。如果表达式的值为false ,那么该语句将不会被运行。

你的程序现在有两个不同的执行路径,取决于salary + other_income 的总和。

否则,如果if 是假的会怎样?

你可以用一个else 来扩展你的if ,如果if 的值为false,就会运行。

在这个例子中,一个客户试图从他们的账户中提取一个金额,如果它不能完成,就会显示一个错误信息。

let mut account_balance = 100.0;
let amount = 101.50;
if account_balance - amount > 0 as f32 {
    account_balance -= amount;
} else {
    println!("Can't withdraw {}", amount);
}

从上面看,else 是紧挨着if 。事实上,没有if ,它就不能存在。

Else if,如果你有一个以上的条件

到目前为止,你已经看到了ifelse ,但还有一个结构else if ,我们可以使用。else if 的意思是,如果if 的评估值为false ,就执行。你可以有任何数量的else if 。下面是如何使用它。

let card_number = 11;
if card_number == 11 {
    println!("Jack");
} else if card_number == 12 {
    println!("Queen");
} else if card_number == 13 {
    println!("King") 
} else if card_number == 14 || card_number == 1 {
    println!("Ace");
} else {
    println!("Card number is {}", card_number);
}

正如你在上面看到的,我们可以在最初的if 之后添加一些else if 结构。

if 作为一个表达式

外面的许多语言都有所谓的三元表达式。这意味着在同一行中,一个值被分配给一个变量。下面是它在其他语言中的样子。

// javascript
let salary = 100;
let tax = 0.2;
let take_home_salary = salary > 200 ? salary * (1 as f32 - tax) : salary;

其价值在于不必用大括号定义一个if ,这可能会占用几行。然而,Rust可以这样做,像这样。

let salary = 100.0;
let tax = 0.2;
let take_home_salary = if salary > 200.0 { salary * (1.0 - tax) } else {salary}; 
println!("Salary is {}", take_home_salary);

注意ifelse 里面的代码没有分号; ,这使得它们成为表达式。所以这里你使用的是基于表达式的ifelse

循环

当你想重复一个语句若干次时,就会用到循环。例如,这可能是。

  • 基于命令的程序,你要求用户输入,直到他们选择退出该程序
  • 迭代的东西。
    • 一个东西的集合,例如 "订单 "和处理每个订单
    • 对一个目录中的文件进行迭代。

正如你所看到的,有很多情况下,循环一组语句是有意义的。Rust为我们提供了许多循环的方法。

loop, 循环语句

使用这种结构,你可以永远重复语句,或者直到程序关闭。下面是示例代码。

loop {
    println!("forever");
} 

break中断一个循环

不过你可以通过给它添加一个break ,来中断循环机制。考虑一下下面的代码,看看它是如何工作的。

let repeat_times: i32 = 5;
let mut index = 0;
loop {
  if index == repeat_times {
      break;
  }
  println!("Loop {}", index + 1);
  index += 1;

}

这个程序将打印以下内容。

Loop 1
Loop 2
Loop 3
Loop 4
Loop 5

前面的代码将打印 "永远",直到你退出程序。

从循环中返回

当你用break 语句退出一个循环时,可以从该循环中返回一个值。

下面是你如何写的。

let mut points = 0;
let result = loop {
    if points >= 10 {
        break points;
    }
    points += 2;
}

这里,points ,每一次迭代都会递增。一旦中断条件为真,points 的当前值将被返回到break points ,并且该值被分配到result

虽然

到目前为止,你已经看到了如何使用loop ,并在其中定义条件,然后调用break 来退出循环。使用while ,你可以将中断条件定义为定义while 的一部分,像这样。

let mut points = 0;
while points < 10 {
    points += 2;
}
println!("Value of points is {}", points);

这里,你的中断条件被定义在while 关键字之后。没有必要使用ifbreak 关键字来退出循环。

哪个版本最容易阅读,loop 还是while?

迭代一个集合

想要重复一个语句的一个常见原因是处理具有序列特征的东西。例如,它可以是。

  • 游泳比赛中的积分列表
  • 购物篮中的一组物品

要定义一个列表,我们可以使用以下语法。

let judges_result = [8, 7, 8, 9, 9];

要在一个列表中列出特定的项目,我们使用方括号,[] 和一个介于0和列表长度-1之间的数字。在上面的例子中,0,1,2,3,4是可寻址的索引。这里有一个例子。

judges_result[2]; //8 

试图用索引5访问,会导致错误。

迭代

那么,我们如何通过iterates_result 迭代?有两种方法我们可以使用。

  • while, 使用while ,这是完全可以的,但是,我们需要跟踪什么时候停止迭代,因为我们可能最终出界。
  • for in.使用这种结构,只要列表中还有项目,我们就可以保证只列出项目。

让我们尝试一下使用while的第一个变体。

let judges_result = [8, 7, 8, 9, 9];
let mut index = 0;
while index < 5 {
  println!("Judge: {0} is {1} ", index + 1, judges_result[index]);
}

现在,让我们把它与使用for in

let judges_result = [8, 7, 8, 9, 9];
for vote in judges_result {
  println!("Judge: {} ", vote);
}

等等,这不是一个公平的比较,索引怎么了?

为了得到索引,我们需要使用一个稍微复杂的结构。

for (i, vote) in judges_result.iter().enumerate() {
     println!("Judge: {0} is {1} ", index + 1, vote);
}

这里发生的情况是,我们调用iter() ,然后再调用enumerate() 。现在我们可以访问一个元组,其中包含索引,i 和我们的值,vote

提醒:元组是一种复杂的数据类型,通过添加小括号和逗号来分隔数值,像这样创建。

  fn create_tuple(a: i32, b: i32) -> (i32, i32) {
      (a, b)
  }
  let (a,b) = create_tuple(2, 4);
  println!("{0} {1}", a, b);

赋值

想象一下,有人从CSV文件中读取POS(销售点系统)今天的条目到一个数组entries 。现在打印出购买的总额和平均价值。

信息:如果里面有一个低于0的值,就应该抛出一个方法,因为这是读取输入时的一个错误。

下面是运行该程序的情况。

Here's today's purchases:
33.5, 7.25, 15.5, 44.9
The total is: 101.15
The average purchase was: 25.2875

解决方案

fn main () {
    let entries = [33.5, 7.25, 15.5, 44.9, -1.0];
    let mut total:f32 = 0.0;
    let mut approved_entries = 0;
    println!("Today's entries are");
    for item in entries {
        if item < 0 as f32 {
            continue;
        }
        total += item;
        print!("{},", item);
        approved_entries += 1; 
    }
    print!("\n");
    println!("The total is {}", total);
    println!("The average purchase was {}", total / approved_entries as f32);
}