流程控制結構
流程控制結構包括:
- 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 }