---
title: "Solidity 错误处理"
description: "了解 Require、Revert 和 Assert 的用法与区别"
---

## Solidity 中的错误处理是如何工作的？

[Solidity](https://www.alchemy.com/overviews/solidity) 使用状态回退异常来处理错误，这种异常会撤销当前调用中对状态所做的所有更改，同时向调用者报告错误。

Solidity 是一种面向对象的编程语言，用于在 Ethereum 等区块链上实现[智能合约](https://www.alchemy.com/overviews/solidity-smart-contract)，它拥有大量函数用于解决编译期或运行期可能出现的底层问题。虽然语法错误检查发生在编译期，但运行时错误较难捕获，且主要发生在合约执行过程中。运行时错误的例子包括除以零类型的错误、数组越界错误等。

实际上，Solidity 中的错误处理确保了**原子性**这一特性。当智能合约调用因错误而终止时，所有状态更改（即对变量、余额等所做的改动）都会被撤销，一直沿调用链向上撤销。

需要注意的是，开发者可以通过[声明接口](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 虚拟机](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 相当于一个[门控检查修饰符](https://www.alchemy.com/overviews/solidity-modifier)，阻止逻辑进一步执行函数，从而产生错误。require 非常适合用于逻辑流程门控以及验证函数的用户输入。

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，则会显示终止性 bug，程序将不会执行。

与 _require_ 和 _revert_ 函数不同，_assert_ 不会返还任何未使用的 gas，相反，_assert_ 函数会先消耗掉全部 gas，然后再将程序恢复到原始状态。有趣的是，在拜占庭分叉之前，_require_ 和 _assert_ 函数的行为完全相同，但会编译为不同的操作码。

#### assert 类型异常

1. 一个值被对零取模或除以零
1. 调用了函数的零初始化变量
1. 将负值或过大的值转换为枚举
1. 访问数组时使用了负数或超出预期范围的索引

_assert_ 语句示例：

#### assert 语句的使用场景

理论上，_assert_ 的使用频率应低于 _require_ 函数。开发者应考虑在以下场景中使用 _assert_ 函数：

- 在更改后验证合约状态
- 避免出现永远不应发生的情况
- 检查溢出和下溢参数
- 检验不变量

## require 与 revert 与 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 训练营之前的绝佳先修课程。
