JavaScript 中的大部分控制结构在 Solidity 中都是可用的,除了 switch 和 goto。 因此 Solidity 中有 if,else,while,do,for,break,continue,return,? : 这些与在 C 或者 JavaScript 中表达相同语义的关键词。
用于表示条件的括号 不可以 被省略,单语句体两边的花括号可以被省略。
注意,与 C 和 JavaScript 不同,Solidity 中非布尔类型数值不能转换为布尔类型,因此 if (1) { ... } 的写法在 Solidity中无效。
下面是一个对0到100求和的合约:
pragma solidity ^0.5.0;
contract Sum {
uint num = 0;
function exec() public returns(uint) {
for (uint i = 0; i < 100; i++) {
num += i;
}
return num;
}
}
Solidity通过Assert
, Require
, Revert
三个关键字来进行异常处理,Solidity使用状态恢复来处理错误,简单来说就是撤销当前操作或者调用对合约状态所做的修改,并且向调用者表明错误。
其中函数assert
和require
用于检查条件是否满足,如果不满足就抛出异常,revert
函数用来标记错误并恢复当前的调用,revert 调用中还可以包含有关错误的详细信息,这个消息会被返回给调用者。
assert
和require
在以太坊拜占庭硬分叉之前行为是一致的,分叉后有细微的差别。
require
函数用于确认条件有效性,例如输入变量,或合约状态变量是否满足条件,或验证外部合约调用返回的值,而assert
应该用于检查内部错误,比如overflow。assert
也被用来预防不应该发生的情况,当assert的错误发生意味着出现了一些出乎意料的错误,甚至是程序失控了,需要修复合约中的问题。
当
require
和revert
调用失败以后会返回剩余的gas,但是assert
并不会。
通过一个合约来看二者的区别。
pragma solidity >=0.5.0;
contract Sharer {
function sendHalf(address addr) public payable returns (uint balance) {
require(msg.value % 2 == 0, "Even value required.");
uint balanceBeforeTransfer = this.balance;
addr.transfer(msg.value / 2);//由于转移函数在失败时抛出异常并且不能在这里回调,因此我们应该没有办法仍然有一半的钱。
assert(this.balance == balanceBeforeTransfer - msg.value / 2);
return this.balance;
}
}
在这个合约中,期望向指定地址转移所请求数值一半的数字资产,如果我们假设这个资产不能拆分到小数点后,只能拆分成整数,那么比如7,9,11这样的奇数就不符合要求。这种不符合要求的情况是符合我们的预期的,发送转移资产的请求可以是任何人,那么发送任何数量都不奇怪,所以在合约的开始通过 require(msg.value % 2 == 0, "Even value required.");
来判断请求转移的资产是否可以对半拆分。
接着按照预期addr.transfer(msg.value / 2);
会转移一半的资产,但是转移后剩余的资产应该是总资产减去转移了的资产,所以用assert(this.balance == balanceBeforeTransfer - msg.value / 2);
来判断是不是符合预期,如果这里报错,那么一定是出现了我们意想不到的问题,可能是转移资产的时候出现了问题,或者什么灵异事件?
处理与 require() 同样的类型,但是需要更复杂处理逻辑的场景,如果有复杂的 if/else 逻辑流,那么应该考虑使用 revert() 函数而不是require()。复杂的场景意味着更多的代码,大量的require只会拖累合约的可读性,所以这是使用revert代替require的好机会。
pragma solidity >=0.5.0;
contract VendingMachine {
function buy(uint amount) payable {
if (amount > msg.value / 2 ether)
revert("Not enough Ether provided.");
// 下边是等价的方法来做同样的检查:
require(
amount <= msg.value / 2 ether,
"Not enough Ether provided."
);
// 执行购买操作
}
}