Skip to main content

流程控制 (Control Flow)

流程控制語句用於控制程式中執行的流向。它們可用於做出決策、重複執行一段程式碼,或提早退出一段程式碼。Move 包含以下流程控制語句(下文將詳細說明):

條件語句 (Conditional Statements)

if 表達式用於在程式中做出決策。它會評估一個 布林表達式,如果表達式為 true,則執行一段程式碼。與 else 配合使用時,如果表達式為 false,它可以執行另一段不同的程式碼。

if 表達式的語法為:

if (<布林表達式>) <表達式>;
if (<布林表達式>) <表達式> else <表達式>;

就像任何其他表達式一樣,如果 if 後面還有其他表達式,則需要分號。else 關鍵字是選填的,除非將產生的值指派給變數,因為所有分支都必須傳回一個值以確保類型安全。讓我們透過以下範例看看 if 表達式在 Move 中是如何運作的:

#[test]
fun test_if() {
let x = 5;

// `x > 0` 是一個布林運算式。
if (x > 0) {
std::debug::print(&b"X is bigger than 0".to_string())
};
}

讓我們看看如何使用 ifelse 來為變數指派值:

#[test]
fun test_if_else() {
let x = 5;
let y = if (x > 0) {
1
} else {
0
};

assert_eq!(y, 1);
}

在此範例中,if 表達式的值被指派給變數 y。如果 x 大於 0,y 被指派為 1;否則,被指派為 0。else 區塊是必要的,因為 if 表達式的兩個分支都必須傳回相同類型的值。省略 else 區塊會導致編譯錯誤,因為它確保考慮了所有可能的分支並維持了類型安全。

條件表達式是 Move 中最重要的流程控制語句之一。它們評估使用者提供的輸入或儲存的資料以做出決策。一個關鍵的使用案例是 assert! 巨集,它檢查條件是否為 true,如果不是則中斷執行。我們稍後將詳細探討這一點。

使用迴圈重複語句 (Repeating Statements with Loops)

迴圈用於多次執行一段程式碼。Move 有兩種內建的迴圈類型:loopwhile。在許多情況下它們可以互換使用,但通常 while 用於預先知道迭代次數的情況,而 loop 用於預先不知道迭代次數或有多個退出點的情況。

迴圈對於處理集合(如向量)或重複執行程式碼直到滿足特定條件非常有用。但是,請務必小心避免無限迴圈,這會耗盡 Gas 限制並導致交易中斷。

while 迴圈

只要相關的布林表達式評估為 true,while 語句就會重複執行一段程式碼。正如我們在 if 中看到的,布林表達式在每次迴圈迭代之前都會被評估。此外,與條件語句一樣,while 迴圈也是一個表達式,如果後面還有其他表達式,則需要分號。

while 迴圈的語法為:

while (<布林表達式>) { <多個表達式>; };

這是一個具有非常簡單條件的 while 迴圈範例:

// 此函式會迭代 `x` 變數直到達到 10,
// 回傳值是達到 10 所需的迭代次數。
//
// 如果 `x` 是 0,則函式會回傳 10。
// 如果 `x` 是 5,則函式會回傳 5。
fun while_loop(mut x: u8): u8 {
let mut y = 0;

// 此迴圈會執行直到 `x` 是 10。
// 如果 `x` 是 10 或更大則永遠不會執行。
while (x < 10) {
y = y + 1;
x = x + 1;
};

y
}

#[test]
fun test_while() {
assert_eq!(while_loop(0), 10); // 10 次
assert_eq!(while_loop(5), 5); // 5 次
assert_eq!(while_loop(10), 0); // 迴圈從未執行
}

無限 loop

現在讓我們想像一種布林表達式始終為 true 的情況。例如,如果我們直接將 true 傳遞給 while 條件。這與 loop 語句的功能類似,不同之處在於 while 會評估一個條件。

#[test, expected_failure(out_of_gas, location=Self)]
fun test_infinite_while() {
let mut x = 0;

// 此迴圈會無限迴圈。
while (true) {
x = x + 1;
};

// 此行永遠不會被執行。
assert_eq!(x, 5);
}

無限 while 迴圈(或條件始終為 truewhile 迴圈)等同於 loop。建立 loop 的語法非常直觀:

loop { <多個表達式>; };

讓我們使用 loop 代替 while 來重寫前面的範例:

#[test, expected_failure(out_of_gas, location=Self)]
fun test_infinite_loop() {
let mut x = 0;

// 此迴圈會無限迴圈。
loop {
x = x + 1;
};

// 此行永遠不會被執行。
assert_eq!(x, 5);
}

無限迴圈在 Move 中很少具有實用性,因為每項運算都會消耗 Gas,無限迴圈必然會導致 Gas 耗盡。如果您發現自己在運用迴圈,請考慮是否有更好的方法,因為許多使用案例可以使用其他流程控制結構更有效地處理。儘管如此,當 loopbreakcontinue 語句結合使用時,對於建立受控且靈活的循環行為可能非常有用。

提早退出迴圈 (Exiting a Loop Early)

正如我們已經提到的,無限迴圈本身是沒什麼用處的。這就是我們引入 breakcontinue 語句的原因。它們分別用於提早退出迴圈和跳過當前迭代的剩餘部分。

break 語句的語法如下(不含分號):

break

break 語句用於停止迴圈的執行並提早退出。它通常與條件語句結合使用,以便在滿足特定條件時退出迴圈。為了說明這一點,讓我們將前面範例中的無限 loop 轉換為外觀和行為更像 while 迴圈的結構:

#[test]
fun test_break_loop() {
let mut x = 0;

// 此迴圈會執行直到 `x` 是 5。
loop {
x = x + 1;

// 如果 `x` 是 5,則退出迴圈。
if (x == 5) {
break // 退出迴圈。
}
};

assert_eq!(x, 5);
}

while 迴圈幾乎一模一樣,對吧?當 x 為 5 時,使用 break 語句退出迴圈。如果我們移除 break 語句,迴圈將永遠執行下去,就像前面的範例一樣。

跳過迭代 (Skipping an Iteration)

continue 語句用於跳過當前迭代的剩餘部分並開始下一次迭代。與 break 類似,它常與條件語句結合使用,以便在滿足特定條件時跳過迭代的其餘部分。

continue 語句的語法如下(不含分號):

continue

下面的範例跳過奇數,僅列印 0 到 10 之間的偶數:

#[test]
fun test_continue_loop() {
let mut x = 0;

// 此迴圈會執行直到 `x` 是 10。
loop {
x = x + 1;

// 如果 `x` 是奇數,則跳過迭代的其餘部分。
if (x % 2 == 1) {
continue // 跳過迭代的其餘部分。
};

std::debug::print(&x);

// 如果 `x` 是 10,則退出迴圈。
if (x == 10) {
break // 退出迴圈。
}
};

assert_eq!(x, 10) // 10
}

breakcontinue 語句在 whileloop 迴圈中都可以使用。

提早傳回 (Early Return)

return 語句用於提早退出 函式 並傳回一個值。它通常與條件語句結合使用,以便在滿足特定條件時退出函式。return 語句的語法為:

return <表達式>

這是一個在滿足特定條件時傳回值的函式範例:

/// 如果 `x` 大於 0 且不是 5 則此函式回傳 `true`,
/// 否則回傳 `false`。
fun is_positive(x: u8): bool {
if (x == 5) {
return false
};

if (x > 0) {
return true
};

false
}

#[test]
fun test_return() {
assert_eq!(is_positive(5), false);
assert_eq!(is_positive(0), false);
assert_eq!(is_positive(1), true);
}

與許多其他語言不同,函式中的最後一個表達式不需要 return 語句。函式區塊中的最後一個表達式會自動傳回。但是,當我們想在滿足特定條件時提早退出函式時,return 語句非常有用。

延伸閱讀