首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在可靠的情况下使用calldata返回值,以及它们何时有用?

如何在可靠的情况下使用calldata返回值,以及它们何时有用?
EN

Ethereum用户
提问于 2022-01-01 22:56:39
回答 2查看 2.4K关注 0票数 2
代码语言:javascript
复制
pragma solidity >=0.7.0 <0.9.0;

contract Storage {

    string[] messages;

    uint256 bidPrice;

    constructor() payable  {
        bidPrice = msg.value * 10001 / 10000;
    }

    function store(string calldata message) public payable {
        if (msg.value < bidPrice) revert( "Payment not enough" );
        messages.push(message);
        bidPrice = bidPrice * 10001 / 10000;
    }

    function retrieve(uint index) public view returns (string calldata){
        return messages[index];
    }

获取错误:

代码语言:javascript
复制
TypeError: Return argument type string storage ref is not implicitly convertible to expected type (type of first return variable) string calldata.
  --> contracts/1_Storage.sol:20:16:
   |
20 |         return messages[index];
   |                ^^^^^^^^^^^^^^^

它只是作为返回类型与memory string一起工作,但是:

  • 为什么我会有这个错误?
  • 有一个calldata返回类型有用吗?在什么情况下?
EN

回答 2

Ethereum用户

发布于 2022-01-02 05:19:02

calldata只适用于输入参数。它不适用于返回的值。

票数 5
EN

Ethereum用户

发布于 2022-01-04 02:42:19

为什么我会有这个错误?

您正在尝试返回一个在存储中而不是在调用数据中的值。要使其工作,编译器必须将您的字符串复制到calldata区域,这是不可能的,因为回调数据是只读的。有记忆就没问题了。

但是,如果调用数据中已经包含了某些内容,则可以返回它。例如,下面是一个函数,它将返回回调字符串的一个片段:

代码语言:javascript
复制
function substring(string calldata input, uint begin, uint end)
    public
    view
    returns (string calldata)
{
    return input[begin:end];
}

有一个calldata返回类型有用吗?在什么情况下?

返回已在调用数据中的内容比返回memory值便宜,因为编译器可以跳过ABI编码。如果可以的话你肯定想这么做。

尽管如此,实际应用这种优化的机会并不是很普遍。这是非常符合情况的。下面是一些我可以从头顶上想到的例子:

  • 缩小重写函数中的返回类型。重写时可以用memory替换calldata,因此您可以拥有一个泛型函数,该函数在接口中返回memory值,然后在特定的实现中使用calldata,而该实现恰好对其输入不做任何操作,并将其返回不变。
  • 封装调用数据逻辑。在某些情况下,您可能有一些逻辑,在进一步处理之前,只需要从回调数据中提取一些内容。例如,您的函数可能只需要一个较大数组的一个片段,并确定它的哪个部分可能足够复杂,从而需要将切片逻辑作为一个单独的函数。如果没有calldata返回,函数将不得不将整个切片复制到内存中。相反,您可以返回片的偏移量和长度,但这基本上是calldata片内部的内容。编译器可以为你做这件事。
  • 带有原始输入的懒惰ABI解码。如果您的契约实现了自定义解码逻辑,能够对calldata值进行完全操作,包括返回它们并将它们存储在局部变量中,则可以保留当您坚持类型系统时通常得到的一些优势。其中一个事实是,在实际使用calldata值之前不会对它们进行解码。您不会在abi.decode()中得到这一点,因为它会急切地解码到内存中。下面是一个接受在原始bytes数组中编码的结构并将其提取到calldata变量的示例: contract { struct { uint;}函数convertToS(字节回调数据输入)内部纯返回(S回调数据结果){程序集{成绩:= input.offset }函数运行(字节调用数据输入)外部纯返回(run数据结果){ calldata =convertToS(输入32:);返回s;}

也许还有更多,但我想你明白了。这不是一个经常使用的特性,但有时它很好。

票数 2
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/117770

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档