用戶定義的值類型

2022-05-11 18:06 更新

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

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

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

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

以下示例說明了一個自定義類型,該類型UFixed256x18表示具有 18 個小數的小數定點類型和一個用于對該類型進行算術運算的最小庫。

// 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函數返回UFixed256x18 與輸入具有相同數據表示的 a,而toUFixed256x18返回 UFixed256x18具有相同數值的 a。

函數類型

函數類型是函數的類型。函數類型的變量可以從函數中賦值,函數類型的函數參數可以用來傳遞函數到函數調用和從函數調用返回函數。函數類型有兩種形式——內部函數和外部函數:

內部函數只能在當前合約內部調用(更具體地說,在當前代碼單元內部,還包括內部庫函數和繼承函數),因為它們不能在當前合約上下文之外執(zhí)行。調用內部函數是通過跳轉到它的入口標簽來實現(xiàn)的,就像在內部調用當前合約的函數一樣。

外部函數由地址和函數簽名組成,它們可以通過外部函數調用傳遞和返回。

函數類型表示如下:

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

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

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

轉換:

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

  • pure函數可以轉換為viewnon-payable函數

  • view函數可以轉換為non-payable函數

  • payable函數可以轉換為non-payable函數

函數類型之間沒有其他轉換是可能的。

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

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

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

請注意,當前合約的公共功能既可以用作內部功能,也可以用作外部功能。要f用作內部函數,只需使用f,如果要使用其外部形式,請使用this.f。

內部類型的函數可以分配給內部函數類型的變量,而不管它是在哪里定義的。這包括合約和庫的私有、內部和公共功能以及免費功能。另一方面,外部函數類型僅與公共和外部合約函數兼容。庫被排除在外,因為它們需要一個delegatecall為其選擇器使用不同的 ABI 約定。接口中聲明的函數沒有定義,因此指向它們也沒有意義。

成員:

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

筆記

外部(或公共)函數曾經有額外的成員 .gas(uint).value(uint). 這些在 Solidity 0.6.2 中已棄用,并在 Solidity 0.7.0 中刪除。而是使用和 分別指定發(fā)送到函數的氣體量或 wei 量。有關詳細信息,請參閱外部函數調用。{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}();
    }
}

顯示如何使用內部函數類型的示例:

// 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;
    }
}

另一個使用外部函數類型的例子:

// 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 或內聯(lián)函數,但尚不支持。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號