Rust 學習筆記:函數和控制流
- Rust 學習筆記:函數和控制流
- 函數(Function)
- 語句和表達式
- 帶返回值的函數
- 注釋
- 控制流
- if 表達式
- 使用 else if 處理多個條件
- 在 let 語句中使用 if
- 循環
- loop
- 從循環中返回值
- 循環標簽消除多個循環之間的歧義
- 帶 while 的條件循環
- 使用 for 循環遍歷集合
Rust 學習筆記:函數和控制流
本篇文章介紹 Rust 的函數、注釋和控制流。
函數(Function)
fn 關鍵字允許聲明新函數。main 函數是程序的入口點。
在 Rust 中定義函數時,輸入 fn,后跟函數名和一組圓括號。花括號告訴編譯器函數體的開始和結束位置。
Rust 并不關心你在哪里定義你的函數,只關心它們被定義在調用者可以看到的作用域中。
示例:
fn main() {println!("Hello, world!");another_function();
}fn another_function() {println!("Another function.");
}
運行結果:
我們可以將函數定義為具有形參的函數,形參是作為函數簽名一部分的特殊變量。
示例:
fn main() {another_function(5);
}fn another_function(x: i32) {println!("The value of x is: {x}");
}
運行結果:
在函數定義中,必須聲明每個參數的類型。當定義多個參數時,用逗號分隔參數聲明。
示例:
fn main() {print_labeled_measurement(5, 'h');
}fn print_labeled_measurement(value: i32, unit_label: char) {println!("The measurement is: {value}{unit_label}");
}
運行結果:
語句和表達式
在 Rust 中,函數體由一系列語句組成,可以由表達式結尾。
- 語句是執行某些操作的指令,但不返回值。
- 表達式計算并返回一個結果值。
函數定義也是語句。
創建變量并使用 let 關鍵字為其賦值是語句。因為語句不返回值,所以不能將 let 語句賦值給另一個變量。
示例:
fn main() {let x = (let y = 6);
}
運行報錯:
let y = 6語句不返回值,因此沒有任何東西可以綁定 x。這與其他語言(如 C)中的情況不同,在這些語言中,賦值返回賦值的值。在這些語言中,你可以寫 x = y = 6,讓 x 和 y 的值都是 6,但在 Rust 中卻不是這樣。
表達式的計算結果是一個值,并且構成了在 Rust 中編寫的大部分其余代碼。考慮一個數學運算,比如 5 + 6,它是一個計算值為 11 的表達式。表達式可以是語句的一部分,例如語句 let y = 6 中的 6 是求值為 6 的表達式。調用函數是一個表達式。調用宏是一個表達式。用大括號創建的新作用域塊是一個表達式。
示例:
fn main() {let y = {let x = 3;x + 1};println!("The value of y is: {y}");
}
表達式
{let x = 3;x + 1
}
是一個塊,在這個例子中,它的值是 4。作為 let 語句的一部分,這個值被綁定到 y 上。注意,x + 1 行末尾沒有分號,這與目前看到的大多數行不同。
表達式不包括結束分號。如果在表達式的末尾添加分號,則將其轉換為語句,然后它將不返回值。
帶返回值的函數
函數可以向調用它們的代碼返回值。我們不為返回值命名,但必須在箭頭(->)后面聲明它們的類型。在 Rust 中,函數的返回值與函數體塊中最終表達式的值是同義的。通過使用 return 關鍵字并指定一個值,可以提前從函數返回,但是大多數函數隱式地返回最后一個表達式。下面是一個返回值的函數示例:
示例:
fn five() -> i32 {5
}fn main() {let x = five();println!("The value of x is: {x}");
}
在 five 函數中沒有函數調用、宏,甚至沒有 let 語句——只有數字 5 本身。這在 Rust 中是一個完全有效的函數。注意,函數的返回類型也被指定為 i32。
另一個示例:
fn main() {let x = plus_one(5);println!("The value of x is: {x}");
}fn plus_one(x: i32) -> i32 {x + 1
}
運行這段代碼將打印 x 的值為 6。但是,如果我們在包含 x + 1 的行末尾放置一個分號,將其從表達式更改為語句,程序將報錯。
注釋
在 Rust 中,慣用的注釋樣式以兩個斜杠開始注釋,并且注釋一直持續到行尾。對于超出單行的注釋,你需要在每一行都包含 //,像這樣:
// So we’re doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain what’s going on.
注釋可以放在它所注釋的代碼上方的單獨一行,也可以放在包含以下代碼的行尾。
fn main() {// I’m feeling lucky todaylet lucky_number = 7; // I’m feeling lucky today
}
控制流
在大多數編程語言中,根據條件是否為真來運行某些代碼以及在條件為真時重復運行某些代碼的能力是基本的構建塊。Rust 代碼執行流的最常見結構是 if 表達式和循環。
if 表達式
if 表達式允許根據條件對代碼進行分支。提供一個條件,然后聲明:“如果滿足此條件,則運行此代碼塊;如果條件不滿足,就不要運行這段代碼。”
示例:
fn main() {let number = 3;if number < 5 {println!("condition was true");} else {println!("condition was false");}
}
運行結果:
同樣值得注意的是,這段代碼中的條件必須是 bool 類型。如果條件不是 bool 類型,我們將得到一個錯誤。例如,嘗試運行以下代碼:
fn main() {let number = 3;if number {println!("number was three");}
}
報錯:
這個錯誤表明 Rust 期望的是 bool 值,但得到的卻是整數。Rust 不會自動嘗試將非布爾類型轉換為布爾類型,必須明確地提供一個布爾值作為它的條件。
使用 else if 處理多個條件
可以通過在 else if 表達式中組合 if 和 else 來使用多個條件。
示例:
fn main() {let number = 6;if number % 4 == 0 {println!("number is divisible by 4");} else if number % 3 == 0 {println!("number is divisible by 3");} else if number % 2 == 0 {println!("number is divisible by 2");} else {println!("number is not divisible by 4, 3, or 2");}
}
當這個程序執行時,它依次檢查每個 if 表達式,并執行條件為 true 的第一個語句體。
在 let 語句中使用 if
因為 if 是一個表達式,我們可以在 let 語句的右側使用它將結果賦值給一個變量。
正確的示例:
fn main() {let condition = true;let number = if condition { 5 } else { 6 };println!("The value of number is: {number}");
}
有可能成為 if 的每個分支的結果的值必須是相同的類型,如果類型不匹配,將會報錯。
錯誤的示例:
fn main() {let condition = true;let number = if condition { 5 } else { "six" };println!("The value of number is: {number}");
}
報錯信息:
if 塊中的表達式求值為整數,else 塊中的表達式求值為字符串。這行不通,因為變量必須有單一類型,Rust 需要在編譯時明確地知道 number 變量是什么類型。
循環
Rust 有三種循環:loop、while 和 for。我們每個都試試。
loop
loop 關鍵字將無限循環,直到顯式停止程序。
示例:
fn main() {loop {println!("again!");}
}
大多數終端都支持鍵盤快捷鍵 Ctrl+C 來中斷陷入連續循環的程序。
幸運的是,Rust 還提供了一種使用代碼跳出循環的方法。可以在循環中放置 break 關鍵字,以告訴程序何時停止執行循環。
continue 關鍵字則在循環中告訴程序跳過此循環迭代中的任何剩余代碼,并進入下一個迭代。
從循環中返回值
可以在用于停止循環的 break 表達式之后添加想要返回的值,該值將從循環中返回。
示例:
fn main() {let mut counter = 0;let result = loop {counter += 1;if counter == 10 {break counter * 2;}};println!("The result is {result}");
}
運行結果:
在循環之前,聲明一個名為 counter 的變量,并將其初始化為 0。然后聲明一個名為 result 的變量來保存循環返回的值。在循環的每次迭代中,我們給計數器變量加 1,然后檢查計數器是否等于 10。如果是,則使用 break 關鍵字和值 counter * 2。循環結束后,使用分號結束將值賦給 result 的語句。最后,在 result 中打印值,在本例中為 20。
也可以從循環內部返回。break 只退出當前循環,而 return 總是退出當前函數。
循環標簽消除多個循環之間的歧義
如果循環中有循環,則在該點中斷并繼續應用于最內層的循環。可以選擇在循環上指定一個循環標簽,然后可以使用 break 或 continue 來指定這些關鍵字應用于有標簽的循環,而不是最內層的循環。循環標簽必須以單引號開始。下面是兩個嵌套循環的例子:
fn main() {let mut count = 0;'counting_up: loop {println!("count = {count}");let mut remaining = 10;loop {println!("remaining = {remaining}");if remaining == 9 {break;}if count == 2 {break 'counting_up;}remaining -= 1;}count += 1;}println!("End count = {count}");
}
外部循環具有 ‘counting_up’ 標簽,它將從 0 數到 2。沒有標簽的內部循環從 10 數到 9。第一個沒有指定標簽的 break 只會退出內循環。
程序打印:
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
帶 while 的條件循環
示例:
fn main() {let mut number = 3;while number != 0 {println!("{number}!");number -= 1;}println!("LIFTOFF!!!");
}
這種結構消除了在使用 loop、if、else 和 break 時所必需的大量嵌套,并且更加清晰。當條件求值為 true 時,代碼運行;否則,退出循環。
還可以使用 while 構造遍歷集合(如數組)的元素。
示例:
fn main() {let a = [10, 20, 30, 40, 50];let mut index = 0;while index < 5 {println!("the value is: {}", a[index]);index += 1;}
}
這里,代碼對數組中的元素進行計數。它從索引 0 開始,然后循環直到到達數組中的最終索引(也就是說,當 index < 5 不再為真時)。
然而,這種方法容易出錯。如果索引值或測試條件不正確,我們可能會導致程序報錯。例如,如果將 a 數組的定義更改為包含 4 個元素,但忘記將條件更新為 while index < 4,則代碼將出現問題。它也很慢,因為編譯器會添加運行時代碼,在循環的每次迭代中執行索引是否在數組邊界內的條件檢查。
使用 for 循環遍歷集合
作為一種更簡潔的替代方法,可以使用 for 循環并為集合中的每個項執行一些代碼。
示例:
fn main() {let a = [10, 20, 30, 40, 50];for element in a {println!("the value is: {element}");}
}
這樣做提高了代碼的安全性,并消除了可能由于超出數組的末尾或不夠遠而丟失某些項而導致的錯誤的可能性。