Solidity 的try/catch 语法和常见的语言中的表现不一样,try { } 块中的代码错误是无法被catch 的,这一点要小心要非常小心。
本文是"深入理解 Solidity 错误"系列的第4篇, 在了解了运行时处理错误的方法后,我们将探讨 Solidity 中一种特殊的错误处理方式:try {} catch {}
。
本文讲介绍如何捕获外部调用的错误,以及在创建新合约时如何使用它。还将了解不同的 catch
子句的用法,他们对应着 Solidity 运行时的那些错误类型。
Solidity 支持使用 try / catch
进行异常处理,但仅限于外部函数调用和合约创建调用。
try catch
语法从版本0.6.2
开始可用。它是作为对低级调用的响应而添加的,许多开发者已经在使用。
在此案例中,调用方(进行调用的智能合约)可以对被调用方(被调用的智能合约)出现的故障和错误做出反应,并使用 try / catch
语法对这些故障做出反应。
try/catch
?在 try
关键字后必须跟一个表达式,该表达式是:
new ContractName()
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^ 0.8 .0;
contract TryCatchExamples {
function tryCatchExternalCall(address target) public {
try Target(target).doSomething() returns(string memory) {
} catch {
}
}
function tryDeployingContract() public {
try new Target() returns(Target) {
} catch {
}
}
}
contract Target {
uint256 _callCounter;
function doSomething() public returns(string memory) {
_callCounter++;
return "state updated successfully";
}
}
同样重要的是,在 try
或 catch
子句中定义的任何局部变量在这些子句之外都是不可访问的。它们的作用域仅限于定义它们的成功或错误块内。
function tryCatchExternalCall(address target) public {
try Target(target).doSomething() returns (string memory) {
uint256 a = 1;
} catch (bytes memory) {
uint256 b = 2;
// Does not compile
// DeclarationError: Undeclared identifier
uint256 c = a;
}
// Does not compile
// DeclarationError: Undeclared identifier
uint256 d = a;
uint256 e = b;
}
}
对于外部调用, try
语句的 return
返回值部分是可选的,即使你尝试调用的外部函数有返回值也是如此。请参阅下面的示例:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TryCatchExamples {
function tryCatchExternalCall(address target) public {
try Target(target).doSomething() {
// if external call was successful, we continue execution here
// we do not care of what was returned.
} catch {
}
}
}
contract Target {
uint256 _callCounter;
function doSomething() public returns (string memory) {
_callCounter++;
return "state updated successfully";
}
}
如果声明了 returns
,它就声明了外部调用返回的内容(类型)。通常会声明一个返回值变量名,以便在 try { ... }
块中处理返回值。
try Target(target).doSomething() returns (string memory message) {
// do something with `message` returned variable
} catch {
}
请注意, returns
内声明的类型必须与函数定义中定义的返回类型一致。举例说明,下面的代码片段将无法编译, 并返回以下错误:
// TypeError: Invalid type, expected string memory but got bytes memory.
try Target(target).doSomething() returns (bytes memory message)
对于通过 try / catch
创建合约,有两种使用 returns
部分的方法。
returns
部分在此案例中,我们只关心合约是否创建成功。如果部署成功,我们就可以在 try
块中做任何我们想做的事情。
function tryDeployingContract() public {
try new Target() {
// a new contract was created successfully.
// we do not care about the address of the new contrat created
// we can do whatever we want in the success block.
} catch (bytes memory) {
}
}
returns
部分如果想获取新创建合约的地址等信息,可以通过定义下面的 returns
语句来实现:
try new <Contract> returns (<Contract>)
在使用 new
关键字创建合约时,在 try
代码块中定义一个 returns
部分,可以获取新创建合约的实例。returns
部分中的类型必须是我们要部署的合约的类型,见下面的示例:
try new Target() returns (Target newContract) {...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!