---
title: "Solidity 錯誤處理"
description: "了解 Require、Revert 與 Assert 的用途與差異"
---

## Solidity 中的錯誤處理如何運作？

[Solidity](https://www.alchemy.com/overviews/solidity) 使用狀態還原例外（state-reverting exceptions）來處理錯誤，此類例外會撤銷目前呼叫中對狀態所做的所有變更，同時向呼叫者標記錯誤。

Solidity 是一種物件導向程式語言，用於在 Ethereum 等區塊鏈上實作[智能合約](https://www.alchemy.com/overviews/solidity-smart-contract)，其具有多種函式可用來處理編譯時期或執行時期可能發生的問題。雖然語法錯誤檢查發生在編譯時期，但執行時期錯誤較難捕捉，主要發生在合約執行過程中。執行時期錯誤的例子包括除以零型錯誤、陣列超出索引錯誤等等。

實際上，Solidity 的錯誤處理確保了**原子性（atomicity）**這項特性。當智能合約呼叫以錯誤終止時，所有狀態變更（即對變數、餘額等所做的變更）都會被還原，並沿著合約呼叫鏈一路往上還原。

值得注意的是，開發者可以透過[宣告介面（interface）](https://www.alchemy.com/overviews/solidity-interface)直接與其他合約互動。在 Ethereum 區塊鏈上，交易具有原子性，意味著交易要嘛完全成功，要嘛對狀態沒有任何影響並完全還原。

## **Solidity 三大主要錯誤處理函式是什麼？**

Solidity 的錯誤處理原則上由三個[**特殊函式**](https://www.alchemy.com/overviews/solidity-functions)管理：assert、require 和 revert。在 0.4.10 版之前，Solidity 只提供單一的 throw 陳述式。

Solidity 的設計目標是 [Ethereum Virtual Machine](https://www.alchemy.com/overviews/what-is-the-ethereum-virtual-machine-evm)（EVM），並受到 C++、Javascript 和 Python 的影響。使用 Solidity，開發者可以建立用於投票、群眾募資、多重簽章錢包，甚至盲拍等用途的合約。

實務上，這代表開發者必須撰寫多個測試函式來檢查底層數值並拋出錯誤，而這種做法並未[針對 gas 進行最佳化](https://www.alchemy.com/overviews/solidity-gas-optimization)。在 Solidity 0.4.10 版發布時，引入了新的錯誤處理結構 assert、require 和 revert，而 throw 則被淘汰。

### require 函式是什麼？

_require_ 函式用於在執行前驗證輸入與條件。舉例來說，若條件為 false，require 函式會立即停止執行。換句話說，require 就像[閘門檢查修飾詞（gate check modifier）](https://www.alchemy.com/overviews/solidity-modifier)，阻止邏輯繼續執行函式，進而產生錯誤。require 非常適合用於邏輯流程的閘門控管，以及驗證函式的使用者輸入。

require 陳述式宣告了執行函式前應滿足的先決條件，這些條件必須在程式碼執行前得到滿足。_require_ 函式接受單一參數，經過評估後會回傳布林值 true 或 false。若因條件為 false 而終止執行，未使用的 gas 會退還給呼叫者，狀態也會還原至原始狀態。此外還可以加入自訂的字串訊息。

**以下是 Solidity 中 require 陳述式的範例：**

<ImageBlock
  src="https://media.alchemy.com/1703928524-how-to-use-the-require-statement.png"
  alt="基本範例展示如何使用 require 陳述式"
  width={859}
  height={291}
  caption="基本範例展示如何使用 require 陳述式"
/>

#### **require 陳述式的使用情境**

出於實務考量，開發者可在下列情境使用 _require_：

1. 驗證來自外部合約的回應
1. 在最終執行前驗證狀態條件
1. 驗證使用者輸入

### revert 陳述式是什麼？

revert 不評估任何條件，也不依賴任何狀態或陳述式。revert 陳述式與 require 陳述式類似，revert 函式可處理與 require 函式相同類型的錯誤，但更適合用於複雜的邏輯閘門。

若呼叫了 _revert_ 陳述式，未使用的 gas 會被退還，狀態也會還原至原始狀態。加入自訂訊息的能力與 _require_ 函式相同。

以下是 Solidity 中 revert 陳述式的範例：

### assert 函式是什麼？

assert 是用來檢查不應該為 false 的程式碼的函式，在防止不可能發生的情況上扮演重要角色。若 assert 函式回傳布林值 true，就會顯示終端錯誤（terminal bug），程式將不會執行。

與 _require_ 及 _revert_ 函式不同的是，_assert_ 不會退還任何未使用的 gas，反而是 _assert_ 函式會先消耗全部的 gas 供應，才將程式還原至原始狀態。有趣的是，在拜占庭（Byzantium）分岔之前，_require_ 與 _assert_ 函式的行為完全相同，但兩者會編譯成不同的操作碼（opcode）。

#### assert 類型例外

1. 數值進行取模或除以零
1. 呼叫了函式中零初始化的變數
1. 將負數或過大的值轉換為列舉（enum）
1. 存取陣列時索引為負數或超出預期範圍

_assert_ 陳述式範例：

#### assert 陳述式的使用情境

理論上，_assert_ 的使用頻率應低於 _require_ 函式。開發者應考慮在以下情境使用 _assert_ 函式：

- 在進行變更後驗證合約狀態
- 避免不應該發生的情況
- 檢查溢位（overflow）與下溢（underflow）參數
- 檢查不變量（invariant）

## **require vs. revert vs. assert**

以下摘要簡要說明了 Solidity 三種錯誤處理函式：_require_、_revert_ 與 _assert_。

#### **require**

- 用於函式開頭
- 驗證非法輸入
- 在執行前驗證狀態條件
- 退還剩餘的 gas

#### **revert**

- 與 require 相同
- 適用於更複雜的邏輯流程閘門（例如複雜的 if-then 區塊）
- 退還剩餘的 gas

#### **assert**

- 用於函式結尾
- 驗證不可能發生的情況
- 對靜態程式碼分析工具至關重要
- 不退還剩餘的 gas

## **如何深入了解 require 與 Solidity 錯誤處理**

有興趣[深入了解 Solidity 錯誤處理](https://www.alchemy.com/overviews/learn-solidity)的開發者，可以報名 Alchemy University 的[免費 Solidity 開發者速成課程](https://university.alchemy.com/?a=382cc2e126)。這個為期 7 週的免費新手訓練營，能幫助 web2 開發者以及完全的新手學習如何撰寫 Solidity 智能合約。若開發者對開發完全陌生，Alchemy University 的[**3 週 JavaScript 速成課程**](https://www.alchemy.com/university/courses/js)是進入 Ethereum 訓練營前很好的先修課程。
