用戶定義的值類型

2022-05-11 18:06 更新

用戶定義的值類型允許在基本值類型上創(chuàng)建零成本抽象。這類似于別名,但具有更嚴(yán)格的類型要求。

用戶定義的值類型使用 定義,其中是新引入類型的名稱,并且必須是內(nèi)置值類型(“基礎(chǔ)類型”)。該函數(shù) 用于從底層類型轉(zhuǎn)換為自定義類型。同樣,該函數(shù)用于從自定義類型轉(zhuǎn)換為基礎(chǔ)類型。type C is VCVC.wrapC.unwrap

該類型C沒有任何運(yùn)算符或綁定的成員函數(shù)。特別是,連運(yùn)算符==都沒有定義。不允許與其他類型進(jìn)行顯式和隱式轉(zhuǎn)換。

這種類型的值的數(shù)據(jù)表示是從底層類型繼承的,底層類型也在 ABI 中使用。

以下示例說明了一個自定義類型,該類型UFixed256x18表示具有 18 個小數(shù)的小數(shù)定點(diǎn)類型和一個用于對該類型進(jìn)行算術(shù)運(yùn)算的最小庫。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.8;

// Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type.
type UFixed256x18 is uint256;

/// A minimal library to do fixed point operations on UFixed256x18.
library FixedMath {
    uint constant multiplier = 10**18;

    /// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked
    /// arithmetic on uint256.
    function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b));
    }
    /// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked
    /// arithmetic on uint256.
    function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b);
    }
    /// Take the floor of a UFixed256x18 number.
    /// @return the largest integer that does not exceed `a`.
    function floor(UFixed256x18 a) internal pure returns (uint256) {
        return UFixed256x18.unwrap(a) / multiplier;
    }
    /// Turns a uint256 into a UFixed256x18 of the same value.
    /// Reverts if the integer is too large.
    function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(a * multiplier);
    }
}

注意UFixed256x18.wrap和如何FixedMath.toUFixed256x18具有相同的簽名但執(zhí)行兩個非常不同的操作:UFixed256x18.wrap函數(shù)返回UFixed256x18 與輸入具有相同數(shù)據(jù)表示的 a,而toUFixed256x18返回 UFixed256x18具有相同數(shù)值的 a。

函數(shù)類型

函數(shù)類型是函數(shù)的類型。函數(shù)類型的變量可以從函數(shù)中賦值,函數(shù)類型的函數(shù)參數(shù)可以用來傳遞函數(shù)到函數(shù)調(diào)用和從函數(shù)調(diào)用返回函數(shù)。函數(shù)類型有兩種形式——內(nèi)部函數(shù)和外部函數(shù):

內(nèi)部函數(shù)只能在當(dāng)前合約內(nèi)部調(diào)用(更具體地說,在當(dāng)前代碼單元內(nèi)部,還包括內(nèi)部庫函數(shù)和繼承函數(shù)),因?yàn)樗鼈儾荒茉诋?dāng)前合約上下文之外執(zhí)行。調(diào)用內(nèi)部函數(shù)是通過跳轉(zhuǎn)到它的入口標(biāo)簽來實(shí)現(xiàn)的,就像在內(nèi)部調(diào)用當(dāng)前合約的函數(shù)一樣。

外部函數(shù)由地址和函數(shù)簽名組成,它們可以通過外部函數(shù)調(diào)用傳遞和返回。

函數(shù)類型表示如下:

function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]

與參數(shù)類型相比,返回類型不能為空——如果函數(shù)類型不應(yīng)返回任何內(nèi)容,則 必須省略整個部分。returns (<return types>)

默認(rèn)情況下,函數(shù)類型是內(nèi)部的,因此internal可以省略關(guān)鍵字。請注意,這僅適用于函數(shù)類型。必須為合約中定義的函數(shù)明確指定可見性,它們沒有默認(rèn)值。

轉(zhuǎn)換:

當(dāng)且僅當(dāng)它們的參數(shù)類型相同、返回類型相同、內(nèi)部/外部屬性相同并且 的狀態(tài)可變性 比 的狀態(tài)可變性更具限制性時,函數(shù)類型A才能隱式轉(zhuǎn)換為函數(shù)類型。尤其:BAB

  • pure函數(shù)可以轉(zhuǎn)換為viewnon-payable函數(shù)

  • view函數(shù)可以轉(zhuǎn)換為non-payable函數(shù)

  • payable函數(shù)可以轉(zhuǎn)換為non-payable函數(shù)

函數(shù)類型之間沒有其他轉(zhuǎn)換是可能的。

關(guān)于payableand的規(guī)則non-payable可能有點(diǎn)混亂,但本質(zhì)上,如果一個函數(shù)是payable,這意味著它也接受零以太幣的支付,所以它也是non-payable。另一方面,non-payable函數(shù)會拒絕發(fā)送給它的以太幣,因此non-payable函數(shù)不能轉(zhuǎn)換為payable函數(shù)。

如果未初始化函數(shù)類型變量,則調(diào)用它會導(dǎo)致Panic 錯誤。如果您在使用后調(diào)用函數(shù),也會發(fā)生同樣的情況delete 。

如果在 Solidity 的上下文之外使用外部函數(shù)類型,它們將被視為function類型,它將地址和函數(shù)標(biāo)識符一起編碼為一個bytes24類型。

請注意,當(dāng)前合約的公共功能既可以用作內(nèi)部功能,也可以用作外部功能。要f用作內(nèi)部函數(shù),只需使用f,如果要使用其外部形式,請使用this.f。

內(nèi)部類型的函數(shù)可以分配給內(nèi)部函數(shù)類型的變量,而不管它是在哪里定義的。這包括合約和庫的私有、內(nèi)部和公共功能以及免費(fèi)功能。另一方面,外部函數(shù)類型僅與公共和外部合約函數(shù)兼容。庫被排除在外,因?yàn)樗鼈冃枰粋€delegatecall為其選擇器使用不同的 ABI 約定。接口中聲明的函數(shù)沒有定義,因此指向它們也沒有意義。

成員:

外部(或公共)函數(shù)具有以下成員:

筆記

外部(或公共)函數(shù)曾經(jīng)有額外的成員 .gas(uint).value(uint). 這些在 Solidity 0.6.2 中已棄用,并在 Solidity 0.7.0 中刪除。而是使用和 分別指定發(fā)送到函數(shù)的氣體量或 wei 量。有關(guān)詳細(xì)信息,請參閱外部函數(shù)調(diào)用。{gas: ...}{value: ...}

顯示如何使用成員的示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.4 <0.9.0;

contract Example {
    function f() public payable returns (bytes4) {
        assert(this.f.address == address(this));
        return this.f.selector;
    }

    function g() public {
        this.f{gas: 10, value: 800}();
    }
}

顯示如何使用內(nèi)部函數(shù)類型的示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

library ArrayUtils {
    // internal functions can be used in internal library functions because
    // they will be part of the same code context
    function map(uint[] memory self, function (uint) pure returns (uint) f)
        internal
        pure
        returns (uint[] memory r)
    {
        r = new uint[](self.length);
        for (uint i = 0; i < self.length; i++) {
            r[i] = f(self[i]);
        }
    }

    function reduce(
        uint[] memory self,
        function (uint, uint) pure returns (uint) f
    )
        internal
        pure
        returns (uint r)
    {
        r = self[0];
        for (uint i = 1; i < self.length; i++) {
            r = f(r, self[i]);
        }
    }

    function range(uint length) internal pure returns (uint[] memory r) {
        r = new uint[](length);
        for (uint i = 0; i < r.length; i++) {
            r[i] = i;
        }
    }
}


contract Pyramid {
    using ArrayUtils for *;

    function pyramid(uint l) public pure returns (uint) {
        return ArrayUtils.range(l).map(square).reduce(sum);
    }

    function square(uint x) internal pure returns (uint) {
        return x * x;
    }

    function sum(uint x, uint y) internal pure returns (uint) {
        return x + y;
    }
}

另一個使用外部函數(shù)類型的例子:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;


contract Oracle {
    struct Request {
        bytes data;
        function(uint) external callback;
    }

    Request[] private requests;
    event NewRequest(uint);

    function query(bytes memory data, function(uint) external callback) public {
        requests.push(Request(data, callback));
        emit NewRequest(requests.length - 1);
    }

    function reply(uint requestID, uint response) public {
        // Here goes the check that the reply comes from a trusted source
        requests[requestID].callback(response);
    }
}


contract OracleUser {
    Oracle constant private ORACLE_CONST = Oracle(address(0x00000000219ab540356cBB839Cbe05303d7705Fa)); // known contract
    uint private exchangeRate;

    function buySomething() public {
        ORACLE_CONST.query("USD", this.oracleResponse);
    }

    function oracleResponse(uint response) public {
        require(
            msg.sender == address(ORACLE_CONST),
            "Only oracle can call this."
        );
        exchangeRate = response;
    }
}

筆記

計劃使用 Lambda 或內(nèi)聯(lián)函數(shù),但尚不支持。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號