流程控制結構

流程控制結構包括:

  • if條件判斷結構
  • loop循環
  • while循環
  • for..in迭代

除此之外,還有其他幾種本節暫不介紹的控制結構。

需要說明的是,Rust中這些結構都是表達式,它們都有默認的返回值(),且if結構和loop循環結構可以指定返回值。

注:【這些結構的默認返回值是()】的說法是不嚴謹的

之所以可以看作是默認返回(),是因為Rust會在每個分號結尾的語句後自動加上小括號(),使得語句看上去也有了返回值。

為了行文簡潔,下文將直接描述為默認返回值。

if..else

if語句的語法如下:


#![allow(unused)]
fn main() {
if COND1 {
  ...
} else if COND2 {
  ...
} else {
  ...
}
}

其中,條件表達式COND不需要加括號,且COND部分只能是布爾值類型。另外,else if分支是可選的,且可以有多個,else分支也是可選的,但最多隻能有一個。

由於if結構是表達式,它有返回值,所以可以將if結構賦值給一個變量(或者其他需要值的地方)。

但是要注意,if結構默認返回Unit類型的(),這個返回值是沒有意義的。如果要指定為其他有意義的返回值,要求:

  • 分支最後執行的那一行代碼不使用分號結尾,這表示將最後執行的這行代碼的返回值作為if結構的返回值
  • 每個分支的返回值類型相同,這意味著每個分支最後執行的代碼都不能使用分號結尾
  • 必須要有else分支,否則會因為所有分支條件判斷都不通過而直接返回if的默認返回值()

下面用幾個示例來演示這幾個要求。

首先是一段正確的代碼片段:


#![allow(unused)]
fn main() {
let x = 33;

// 將if結構賦值給變量a
// 下面if的每個分支,其返回值類型都是i32類型
let a = if x < 20 {
  // println!()不是該分支最後一條語句,要加結尾分號
  println!("x < 20");
  // x+10是該分支最後一條語句,
  // 不加分號表示將其計算結果返回,返回類型為i32
  x + 10  
} else if x < 30 {
  println!("x < 30");
  x + 5   // 返回x + 5的計算結果,返回類型為i32
} else {
  println!("x >= 30");
  x     // 直接返回x,返回類型為i32
};  // if最後一個閉大括號後要加分號,這是let的分號
}

下面是一段將if默認返回值()賦值給變量的代碼片段:


#![allow(unused)]
fn main() {
let x = 33;

// a被賦值為`()`
let a = if x < 20 {
  println!("x < 20");
};
println!("{:?}", a);  // ()
}

下面不指定else分支,將報錯:


#![allow(unused)]
fn main() {
let x = 33;

// if分支返回i32類型的值
// 但如果沒有執行if分支,則返回默認值`()`
// 這使得a的類型不是確定的,因此報錯
let a = if x < 20 {
  x + 3   // 該分支返回i32類型
};
}

下面if分支和else if分支返回不同類型的值,將報錯:


#![allow(unused)]
fn main() {
let x = 33;

let a = if x < 20 {
  x + 3      // i32類型
} else if x < 30 {
  "hello".to_string()  // String類型
} else {
  x   // i32類型
};
}

由於if的條件表達式COND部分要求必須是布爾值類型,因此不能像其他語言一樣編寫類似於if "abc" {}這樣的代碼。但是,卻可以在COND部分加入其他語句,只要保證COND部分的返回值是bool類型即可。

例如下面的代碼。注意下面使用大括號{}語句塊包圍了if的COND部分,使得可以先執行其他語句,在語句塊的最後才返回bool值作為if的分支判斷條件。


#![allow(unused)]
fn main() {
let mut x = 0;
if {x += 1; x < 3} {
  println!("{}", x);
}
}

這種用法在if結構上完全是多此一舉的,但COND的這種用法也適用於while循環,有時候會有點用處。

while循環

while循環的語法很簡單:


#![allow(unused)]
fn main() {
while COND {
  ...
}
}

其中,條件表達式COND和if結構的條件表達式規則完全一致。

如果要中途退出循環,可使用break關鍵字,如果要立即進入下一輪循環,可使用continue關鍵字。

例如:


#![allow(unused)]
fn main() {
let mut x = 0;

while x < 5 {
  x += 1;
  println!("{}", x);
  if x % 2 == 0 {
    continue;
  }
}
}

根據前文對if的條件表達式COND的描述,COND部分允許加入其他語句,只要COND部分最後返回bool類型即可。例如:


#![allow(unused)]
fn main() {
let mut x = 0;

// 相當於do..while
while {println!("{}", x);x < 5} {
  x += 1;
  if x % 2 == 0 {
    continue;
  }
}
}

最後,while雖然有默認返回值(),但()作為返回值是沒有意義的。因此,不考慮while的返回值問題。

loop循環

loop表達式是一個無限循環結構。只有在loop循環體內部使用break才能終止循環。另外,也使用continue可以直接跳入下一輪循環。

例如,下面的循環結構將輸出1、3。


#![allow(unused)]
fn main() {
let mut x = 0;
loop {
  x += 1;
  if x == 5 {
    break;
  }
  if x % 2 == 0 {
    continue;
  }
  println!("{}", x);
}
}

loop也有默認返回值(),可以將其賦值給變量。例如,直接將上例的loop結構賦值給變量a:


#![allow(unused)]
fn main() {
let mut x = 0;
let a = loop {
  ...
};

println!("{:?}", a);   // ()
}

作為一種特殊情況,當在loop中使用break時,break可以指定一個loop的返回值


#![allow(unused)]
fn main() {
let mut x = 0;
let a = loop {
  x += 1;
  if x == 5 {
    break x;    // 返回跳出循環時的x,並賦值給變量a
  }
  if x % 2 == 0 {
    continue;
  }
  println!("{}", x);
};
println!("var a: {:?}", a); // 輸出 var a: 5
}

注意,只有loop中的break才能指定返回值,在while結構或for迭代結構中使用的break不具備該功能。

for迭代

Rust中的for只具備迭代功能。迭代是一種特殊的循環,每次從數據的集合中取出一個元素是一次迭代過程,直到取完所有元素,才終止迭代。

例如,Range類型是支持迭代的數據集合,Slice類型也是支持迭代的數據集合。

但和其他語言不一樣,Rust數組不支持迭代,要迭代數組各元素,需將數組轉換為Slice再進行迭代。


#![allow(unused)]
fn main() {
// 迭代Range類型:1..5
for i in 1..5 {
  println!("{}", i);
}

let arr = [11, 22, 33, 44];
// arr是數組,&arr轉換為Slice,Slice可迭代
for i in &arr {
  println!("{}", i);
}
}

標籤label

可以為loop結構、while結構、for結構指定標籤,break和continue都可以指定標籤來確定要跳出哪一個層次的循環結構。

例如:


#![allow(unused)]
fn main() {
// 'outer和'inner是標籤名
'outer: loop {
  'inner: while true {
    break 'outer;  // 跳出外層循環
  }
}
}

需注意,loop結構中的break可以同時指定標籤和返回值,語法為break 'label RETURN_VALUE

例如:


#![allow(unused)]
fn main() {
let x = 'outer: loop {
  'inner: while true {
    break 'outer 3;
  }
};

println!("{}", x);   // 3
}