Rust 迴圈與流程控制
- 重點摘要
- if ... else if ... else
- 三元運算 ( Ternary Operator)
- 迴圈
- loop Loop: 無限迴圈,除非遇到 break
- for Loop
- while Loop
- pattern matching: 類似其他語言的 switch/case 分支結構
- if let: 若(if) 讓(let) matching condition 成真則做後續動作
if else
- condition 的部分可以省略括號,加上小括號的話 IDE 會出現移除的建議。
- 與 Python 不同的是,Rust condition 必須是 boolean。(Python 非空字串集合等視為 True)
Rust if else example
let x = 5;
let y = 6;
if x %2 ==0 && y %2 ==0 {
println!("both even");
}else if x %2 !=0 && y %2 !=0 {
println!("both odd");
}else{
println!("different");
}
三元運算子 ( Ternary Operator )
- 也可算是一種 if else 變形。 Rust 支援 let statement 中使用 if
- 不同分支的回傳結果必須相同。
Rust Ternary Operator
//Syntax
let variable = if condition { expression_A } else { expression_B }; //注意最末端有一個分號
let num = 10;
let result = if num%2==0 {"even"} else {"odd"};
- Rust 三元運算其實可以擴大成 block 的寫法。
//Syntax
let num = 15;
let result =
if num%2==0 {
if num > 10 {
"Even and > 10"
}else{
"Even and < 10"
}
} else {
if num > 10 {
"Odd and > 10"
}else{
"Odd and < 10"
}
};
println!("num is {}", result );
迴圈 ( Loops )
- 迴圈
- for Loop
- while Loop
- loop Loop: 無限迴圈,除非遇到 break
- break and continue
- break: Rust 的 break 後方可以搭配 expression,用來拋出回傳值 。
for Loop
- 當條件發生時中斷迴圈
for loop syntax
//for each
for element in list {
statements...
}
//for in range
for element in (from..to) {
statements...
}
for element in (from..to).rev() {
statements...
}
loop Loop
- 無限迴圈,直到遇到 break
- loop label
loop loop syntax
'loop_label: loop{
statements...
//break;
//break expression;
//break 'loop_label;
}
while Loop
- 當條件發生時中斷迴圈
while loop syntax
while condition {
statements...
}
fn main() {
let mut count_down = 10;
while count_down != 0 {
println!("{count_down}!");
count_down -= 1;
}
println!("Fire!");
}
break continue 中斷迴圈
- Rust 以 break 關鍵字中斷迴圈的語法有三種
- 單純地中斷迴圈
- 多重迴圈下中斷內層迴圈並繼續指定外層迴圈( 搭配 loop label 使用 )
- 中斷並拋出回傳值
Break Syntax
- label :
label name 前方要加一個 ' (單引號),中間不可有空白。
label name 後方要接一個 : (冒號)。
'loop_label: loop{
statements...
//break;
//break expression;
//break 'loop_label;
}
Break with lable Example
pub fn break_basic() {
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let time = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
'outter: for element in data {
if element > 7 {
break;
}
'inner: for time in time {
if time > 5 {
continue 'outter; //go to label 'outter
// break 'outter; //jump to label 'outter and stop
}
println!(
"{} x {} ={} ",
element.to_string(),
time.to_string(),
element * time
)
}
print!("**** end" ); //never print. break while e>7 or t>5.
}
}
break 中斷並拋出回傳值
首先這個語法讓人想到的第一個疑惑是,
直接 return 回傳值就好了,為何要由 break 來回傳呢?Rust 是 expressive language。
這意味著 Rust 的許多結構甚至是單傳的由大括號包覆的區塊都可以有回傳值。
所以,
第一是 Rust 本身語言的特性使然。
第二,return 是將整個 function 終結, 而 break 僅中斷迴圈。這意味著 function 還活著,還能繼續接下來的 statements。
也就是說,當 loop 並非是 function 的最終行時,用途便出現了。另外,Break with return 似乎只能用在 loop loops。
也就是中斷無限迴圈並給予於一個中斷狀態值。
Block with return
let result = {
let a = 10;
let b = 20;
a * b // no ending ';', this is the last expr, the retval
};
println!("{}" , result.to_string());
Break with return Example
pub fn break_with_return() {
let mut rng = rand::thread_rng();
let mut tmp = 0;
let salt = loop {
tmp = rng.gen::<i32>();
if tmp > 100 {
break tmp;
//這邊用 break with return,得到結果並繼續
//若用 return,則 function 整個中斷。後面列印,gen_key 皆不會執行。
}
};
println!("Found base>100 : {}", salt);
let key = gen_key(salt);
println!("sale: {}", key)
}
傳統 OOP 實作比較
pub fn break_with_return2() {
let salt = rand_num();
println!("Found base>100 : {}", salt);
let key = gen_key(salt);
println!("sale: {}", key)
}
fn rand_num() -> i32 {
let mut rng = rand::thread_rng();
let mut tmp = 0;
let mut salt = 0;
loop {
tmp = rng.gen::<i32>();
if tmp > 100 {
salt = tmp;
return salt;
}
}
}
## Pattern match: 類似其他語言的 switch/case 分支結構
match example
enum QuestionType {
DVG(u32),
Multiple(Vec<u32>),
FillIn(String),
}
fn fetch_answer(question: QuestionType) -> String {
match question {
QuestionType::DVG(dvg) => {
return extract_dvg_answer(dvg);
}
QuestionType::Multiple(multi) => {
return extract_multi_answer(multi);
}, //大括號封裝,所以逗號可省略
QuestionType::FillIn(fillin) => {
return extract_fillin_answer(fillin);
}
}
}
fn extract_dvg_answer(dvganswer: u32) -> String {
return dvganswer.clone().to_string();
}
fn extract_fillin_answer(fillin: String) -> String {
return fillin;
}
fn extract_multi_answer(multi: Vec<u32>) -> String {
let mut combine = "".to_owned();
for item in multi {
combine.push_str(&";".to_owned());
combine.push_str(&item.to_string());
}
return combine;
}
pub fn match_exercise() {
let dvg = QuestionType::DVG(10);
let multi = QuestionType::Multiple(vec![1, 3, 5]);
let fillin = QuestionType::FillIn("Hello, I'm Totem.".to_owned());
println!("-----");
println!("DVG answer is : {}", fetch_answer(dvg));
println!("Multiple answer is : {}", fetch_answer(multi));
println!("FillIn answer is : {}", fetch_answer(fillin));
//DVG answer is : 10
//Multiple answer is : ;1;3;5
//FillIn answer is : Hello, I'm Totem.
}
If let condition
若讓 matching condition 成真則做後續動作。
是 pattern matching 的 syntax sugar。這個語法應該只用在簡化 Patter Matching 情境下。
且意思不同於單純的 if 句子(後面接的是 boolean expression)。
註: 目前應該是因為對 Rust 仍在初學階段,語法原則不明白。所以使用上有非預期的情境出現。
例如:
pattern_condition 中等號兩側 expression 不可互換,
左側必須是 enum 的變體 instance 列舉。
右側應該限定是輸入的條件參數。
pattern_condition : 僅限是 enum 的比對,不同於一般的 if condition。**目前我傾向於少用**,畢竟我對這語法不了解。
if let syntax
- 若(if) 讓/當(let) condition 成真則做後續動作,不然(else) 做另一動作。
if let pattern_condition {
go();
} else {
wait();
}
if let syntax: 官方範例
// 未使用 sugar 的原始長相:
let onfig_max = Some(3u8);
match onfig_max {
Some(max)=> println!("最大值為 {}", max),
_=>(), // do nothing
}
// 等同於
let onfig_max = Some(3u8);
if let Some(max)= onfig_max {
println!("最大值為 {}", max);
} else {
do_nothing();
}
pattern_condition Syntax
- enum_variant : 這邊是 enum 的 static instance。
- instance : 這邊指的是情境代入的狀態。
- 這邊似乎左右 expressions 不可互換。
- 這邊不同於 boolean expression。
enum_variant = instance