Solidity-16-Expressions and Control Structures
控制结构
从花括号语言中知道的大多数控制结构都可以在 Solidity 中使用:
有:if、else、while、do、for、break、continue、return,具有 C 或 JavaScript 中已知的常用语义。
Solidity 还支持 try/catch 语句形式的异常处理,但仅适用于外部函数调用和合约创建调用。 可以使用 revert 语句创建错误。
条件句不能省略括号,但单语句体周围的花括号可以省略。
请注意,没有像 C 和 JavaScript 那样从非布尔类型转换为布尔类型,因此 if (1) { ... } 不是有效的 Solidity。
函数调用
内部函数调用
当前合约的函数可以直接(“内部”)调用,也可以递归调用,如以下无意义的示例所示:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 =0.6.2 =0.4.0 uint) data;
function f() public {
set({value: 2, key: 3});
}
function set(uint key, uint value) public {
data[key] = value;
}
}
省略功能参数名称
未使用的参数(尤其是返回参数)的名称可以省略。
这些参数仍将存在于堆栈中,但无法访问。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 =0.7.0 =0.7.0 =0.5.0 =0.4.22 =0.5.0 =0.5.0 =0.5.0 >, >=) 代替整数除法和乘以 2 的幂时尤其明显。
例如 `type(uint256).max = x.length 或 i =0.5.0 msg.value / 2 ether)
revert("Not enough Ether provided.");
// Alternative way to do it:
require(
amount =0.8.1;
interface DataFeed { function getData(address token) external returns (uint value); }
contract FeedConsumer {
DataFeed feed;
uint errorCount;
function rate(address token) public returns (uint value, bool success) {
// Permanently disable the mechanism if there are
// more than 10 errors.
require(errorCount < 10);
try feed.getData(token) returns (uint v) {
return (v, true);
} catch Error(string memory /*reason*/) {
// This is executed in case
// revert was called inside getData
// and a reason string was provided.
errorCount++;
return (0, false);
} catch Panic(uint /*errorCode*/) {
// This is executed in case of a panic,
// i.e. a serious error like division by zero
// or overflow. The error code can be used
// to determine the kind of error.
errorCount++;
return (0, false);
} catch (bytes memory /*lowLevelData*/) {
// This is executed in case revert() was used.
errorCount++;
return (0, false);
}
}
}
try 关键字后面必须跟一个表示外部函数调用或合约创建的表达式(new ContractName())。
表达式内部的错误不会被捕获(例如,如果它是一个还涉及内部函数调用的复杂表达式),只会在外部调用本身内部发生还原。
后面的返回部分(可选)声明了与外部调用返回的类型匹配的返回变量。
如果没有错误,则分配这些变量,并且合约的执行在第一个成功块内继续。如果到达成功块的末尾,则在 catch 块之后继续执行。
Solidity 根据错误类型支持不同类型的 catch 块:
catch Error(string memory reason) { ... }:如果错误是由 revert("reasonString") 或 require(false, "reasonString") (或导致此类异常的内部错误)引起的,则执行此 catch 子句.
catch Panic(uint errorCode) { ... }:如果错误是由恐慌引起的,即失败的断言、被零除、无效的数组访问、算术溢出等,将运行此 catch 子句。
catch (bytes memory lowLevelData) { ... }:如果错误签名与任何其他子句不匹配,如果在解码错误消息时出错,或者如果没有提供错误数据和异常,则执行此子句。在这种情况下,声明的变量提供对低级错误数据的访问。
catch { ... }:如果您对错误数据不感兴趣,您可以使用 catch { ... }(即使作为唯一的 catch 子句)代替前面的子句。
计划在未来支持其他类型的错误数据。字符串 Error 和 Panic 当前按原样解析,不被视为标识符。
为了捕获所有错误情况,您必须至少有子句 catch { ...} 或子句 catch (bytes memory lowLevelData) { ... }。
在 return 和 catch 子句中声明的变量只在后面的块中。
- 笔记
如果在 try/catch 语句中的返回数据解码过程中发生错误,这会导致当前执行的合约出现异常,因此不会在 catch 子句中捕获。如果在catch Error(string memory reason)的解码过程中出现错误,并且有一个低级的catch子句,这个错误就会被捕获到那里。
- 笔记
如果执行到达一个catch-block,则外部调用的状态改变效果已经恢复。如果执行到达成功块,则效果不会恢复。如果效果已恢复,则在 catch 块中继续执行或 try/catch 语句本身的执行恢复(例如,由于上述解码失败或由于未提供低级 catch 子句)。
- 笔记
呼叫失败背后的原因可能是多方面的。不要假设错误消息直接来自被调用的合约:错误可能发生在调用链的更深处,而被调用的合约只是转发了它。此外,这可能是由于气体不足的情况,而不是故意的错误情况:调用者始终在调用中保留至少 1/64 的气体,因此即使被调用的合约耗尽气体,调用者还剩一些气。
参考资料
https://docs.soliditylang.org/en/latest/control-structures.html