我目前正在使用solc v0.4.25,在这里不可能返回结构,因此返回多个值的唯一方法是一个元组。
我被建议给每个返回的值命名,以使代码更容易阅读,并且不容易出错。
但是,它最终将迫使我为系统中的每个返回值指定名称,即使只返回一个值。
为了澄清上述情况,以下是两个例子:
function withoutNames(uint x) pure returns (uint, uint) {
uint y = 2 * x;
uint z = 3 * x;
return (y, z);
}
function withNames(uint x) pure returns (uint y, uint z) {
y = 2 * x;
z = 3 * x;
}我的问题是:
对于如何处理返回值命名,是否有正式的编码约定(或社区中公认的标准)?
在我看来,选择是:
非常感谢你的建议。
发布于 2019-02-27 14:15:49
两种方法的“返回字节缓冲区”完全相同。
唯一的区别是生成的契约ABI文件,“外部”契约调用方( js / php / python / etc )可以使用该文件自动为我们的方法的返回分配变量名。
Ie:
var callResultWithNames = await contract.methods.withNames(1).call();
var y = callResultWithNames.y;
var z = callResultWithNames.z;
// array indexing also works
// var y = callResultWithNames[0];
// var z = callResultWithNames[1];对比
var callResultWithoutNames = await contract.methods.withoutNames(1).call();
var y = callResultWithoutNames[0];
var z = callResultWithoutNames[1];function withoutNames(uint x) public pure returns (uint, uint);
ABI JSON ->
"name": "withoutNames",
"outputs": [
{
"name": "", <<<<
"type": "uint256"
},
{
"name": "", <<<<
"type": "uint256"
}
],function withNames(uint x) public pure returns (uint y, uint z);
ABI JSON ->
"name": "withNames",
"outputs": [
{
"name": "y", <<<<
"type": "uint256"
},
{
"name": "z", <<<<
"type": "uint256"
}
],当讨论从契约中直接在实体代码中调用这样的方法时,我们已经将返回的值分配给变量(A ),或者使用staticcall (B)通过程序集读取返回的缓冲区,在这种情况下,我们不需要变量名。
(
中
var x,y = contract.withoutNames(1);中
assembly {
let success := staticcall(
500000, // Gas to use for this call
toAddress , // To addr
callBufferPointer, // Inputs are stored at current ptr location
callDataLength, // input length
0,
0)
// copy result byte size from return value ( ignore first 32 bytes ) ( next 32 bytes )
returndatacopy(
thisOutputPointer,
0,
returndatasize()
)
// manipulate and use the returned data stored at thisOutputPointer address any way we want.
}即使函数声明中有返回值的名称,在web3加载时也会自动生成这些对象,我个人认为一步一步地读取代码时非常分散,并且通常更喜欢在方法末尾使用返回语句。
发布于 2019-02-27 17:04:30
从solidity代码的角度来看,在以下情况下选择使用命名返回值:
1)它大大提高了可读性。例如,任何定义为
function foo( ... ) returns (bool success)立即被理解。另一种选择:
function foo( ... ) returns (bool) // true if successful可读性要低得多。
2)到目前为止,最重要的是,当您计划使用局部变量来优化气体的计算时,这意味着状态变量。
如果您需要这个函数:
function foo( ... ) returns (uint) {
return(globalPubliCVariable*globalPubliCVariable*globalPubliCVariable + 2*globalPubliCVariable + 1);
}众所周知,使用局部临时变量可以在煤气成本方面有很大的优势:
function foo( ... ) returns (uint) {
uint res;
res = globalPublicVariable;
res = res*res*res+ 2*res + 1;
return(res);
}但是,您可以使用命名返回值作为临时局部变量,在燃气成本方面有进一步的优势:
function foo( ... ) returns (uint res) {
res = globalPublicVariable;
res = res*res*res+ 2*res + 1;
}不同之处在于您没有为中间结果创建任何局部变量。(可以证明,这样可以最小化堆栈的深度)。
。
编辑#1以处理各种注释
要更好地解释第二点,您需要知道,当调用函数时,调用方在堆栈上推送参数、返回值的空间、返回地址(而不是按这个顺序)。在此级别上,具有命名的或未命名的返回值完全相同。当将控件传递给被调用的函数时,如果被调用的函数声明了一些局部变量,它们也会被推到堆栈上。这意味着,如果您没有这样的局部变量,您的堆栈就会比在有它们的情况下小。在这种情况下,有时可以避免声明用于使用临时命名返回值存储中间结果的新局部变量(它们已经存在,并且只在遇到返回时写入.如果他们没有名字的话。如果他们,相反,被命名..。它们可以作为局部变量在调用的例程中使用,在返回之前写入它们的最后一个值应该是调用方读取的返回值。所以:你可以在回来之前写和读任何你想要的东西。而不使用额外的堆栈空间。当然,您必须确保在返回时,您应该已经在其中写入了正确的返回值。就在回来之前。。
编辑#2 -重要但错误的评论
“我想你没注意到我的意思。无论您的局部变量是在函数的头中声明,还是在函数的正文中声明,都可以优化堆栈大小。对于在标头中声明返回变量的函数的每个示例,都可以实现一个等价的函数,该函数在主体中声明返回变量。这两种实现只是在语法上不同,而不是在编译的代码中,因此在运行时行为上没有区别。-良好振动“
这是绝对错误的。在任何(任何)情况下,在执行任何被调用函数期间深度的堆栈是:
RLSD = sizeof(function parameters) + sizeof(return address) + sizeof(return values) + sizeof(local temporary variables in the called function)这是不可能有低于RLSD的情况下,后者是零。句号。
临时变量的分配不是语法上的糖:它是一个深层次的结构选择。您可以(有时)避免它重用为返回值保留的空间(反正它在那里),作为被调用函数执行中的临时缓冲区。而且你可以做这个选择,前提是你的返回值是“命名的”。
https://ethereum.stackexchange.com/questions/67708
复制相似问题