文章目錄
- 引用類型
- 數組切片
- 結構體
引用類型
數組切片
數組切片是對數組中連續部分的一個視圖。它的語法為 x[start:end]
,其中 start
和 end
是表達式,結果類型為 uint256
(或者可以隱式轉換為 uint256
)。切片的第一個元素是 x[start]
,最后一個元素是 x[end - 1]
。
如果 start
大于 end
,或者 end
大于數組的長度,則會拋出異常。
start
和 end
都是可選的:start
默認為 0,end
默認為數組的長度。
數組切片沒有成員。它們可以隱式地轉換為其底層類型的數組,并支持索引訪問。索引訪問相對于切片的起始位置,而不是底層數組的絕對位置。
數組切片沒有類型名稱,這意味著沒有變量可以將數組切片作為類型,它們僅存在于中間表達式中。
注意
目前,數組切片僅在 calldata
類型的數組上可用。數組切片在 ABI 解碼函數參數中的輔助數據時非常有用:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.5 <0.9.0;contract Proxy {/// @dev 代理管理的客戶端合約地址address client;constructor(address client_) {client = client_;}/// 在對地址參數進行基本驗證后,轉發調用到客戶端實現的 "setOwner(address)"。function forward(bytes calldata payload) external {bytes4 sig = bytes4(payload[:4]);// 由于截斷行為,bytes4(payload) 的效果是一樣的。// bytes4 sig = bytes4(payload);if (sig == bytes4(keccak256("setOwner(address)"))) {address owner = abi.decode(payload[4:], (address));require(owner != address(0), "Address of owner cannot be zero.");}(bool status,) = client.delegatecall(payload);require(status, "Forwarded call failed.");}
}
結構體
Solidity 提供了一種定義新類型的方式,即結構體(struct),如下例所示:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;// 定義一個具有兩個字段的新類型。
// 在合約外部聲明結構體允許它被多個合約共享。
// 這里其實不需要這樣做。
struct Funder {address addr;uint amount;
}contract CrowdFunding {// 結構體也可以在合約內部定義,這樣它只在合約內部以及派生合約中可見。struct Campaign {address payable beneficiary;uint fundingGoal;uint numFunders;uint amount;mapping(uint => Funder) funders;}uint numCampaigns;mapping(uint => Campaign) campaigns;function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {campaignID = numCampaigns++; // campaignID 是返回的變量// 我們不能使用 "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)"// 因為右側會創建一個內存結構體 "Campaign",其中包含一個映射。Campaign storage c = campaigns[campaignID];c.beneficiary = beneficiary;c.fundingGoal = goal;}function contribute(uint campaignID) public payable {Campaign storage c = campaigns[campaignID];// 創建一個新的臨時內存結構體,并用給定的值初始化,// 然后將其復制到存儲中。// 注意,你也可以使用 Funder(msg.sender, msg.value) 來初始化。c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});c.amount += msg.value;}function checkGoalReached(uint campaignID) public returns (bool reached) {Campaign storage c = campaigns[campaignID];if (c.amount < c.fundingGoal)return false;uint amount = c.amount;c.amount = 0;c.beneficiary.transfer(amount);return true;}
}
該合約并沒有提供一個完整的眾籌合約功能,但它包含了理解結構體所需的基本概念。結構體類型可以在映射和數組中使用,結構體本身也可以包含映射和數組。
需要注意的是,結構體不能包含其自身類型的成員,盡管結構體可以作為映射成員的值類型,或者可以包含其類型的動態大小數組。這一限制是必要的,因為結構體的大小必須是有限的。
在所有函數中,結構體類型都被分配給了一個數據位置為 storage
的局部變量。這并不會復制結構體,而只是存儲一個引用,以便對局部變量成員的賦值實際上會寫入狀態。
當然,你也可以直接訪問結構體的成員,而不必將其分配給局部變量,就像 campaigns[campaignID].amount = 0
這樣。
注意
在 Solidity 0.7.0 及之前的版本,允許內存結構體包含存儲類型的成員(如映射),并且像上例中的 campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)
這樣的賦值會正常執行,但會默默跳過這些成員。