スマートコントラクトプログラミング言語であるSolidityのMappingについて記載します。
SolidityのMappingについて
Solidity のマッピングは、他のプログラミング言語におけるハッシュテーブルやディクショナリー機能のように動作します。
SolidityのMappingでは、Key(キー)とValue(値)のペアの形でデータを格納し、スマートコントラクトでは主に一意のイーサリアム・アドレスを関連する値の型に関連付けするために使用されています。
Mappingのルール
- マッピングは、ストレージのタイプのみを持つことができ、一般的に状態変数に使用されます。
- マッピングは public とマークすることができます。Solidity は自動的にそのためのgetter関数を作成します。
Mappingの利用例
マッピングの構文は下記のとおりです
1 |
mapping(_KeyType => _ValueType) |
マッピングはキーと値(それぞれ事前に定義されたタイプ)のテーブルと言えるかもしれません。
ここで実際のデータ型や変数の例を見てみたいと思います。
1 |
mapping (address => uint) points; |
上記のマッピングはaddressをKey、uintをデータタイプとしてpointsをマッピングとして宣言しています。
このマッピングを宣言することによって、下記のような空のテーブルを作成した形になります。
address (key) | points (value) |
実際の値も入れて確認してみましょう。
1 2 3 4 5 6 7 8 |
//addressをKey、uintをデータタイプとしてpointsをmapping型として定義 mapping (address => uint) points; //ETHのアドレスをmember変数に格納 address member = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"; //上記で定義したmapping型のpointsのkeyにmember変数(アドレス)を指定し、1000を設定。 points[player] = 1000; |
テーブルを見てみましょう。このような形でデータを持つことになります。
addressをキーにしてsocresのデータを確認することが出来るようになります。
address (key) | points (value) |
0x5B,,,,eddC4 | 1000 |
テーブルとして考えるとわかりやすいですよね。
Mappingのデフォルト値
まだ明示的に値が設定されていないキーに対して、設定した場合はデフォルト値が設定されます。
Solidityのすべてのデータ型には、値が割り当てられていない場合のデフォルト値があります。
「未定義」または「NULL」値の概念は Solidity には存在しませんが、新しく宣言された変数には、その型に依存するデフォルト値が常に存在します。
Solidityのデフォルト値は下記のとおりです。
uint | 0 |
int | 0 |
fixed | 0.0 |
string | “” |
boolean | fakse |
enum | enumの最初の要素 |
address | 0x0 |
array | 動的配列として設定 |
mapping | 空のマッピングとして設定 |
struct | 構造体で定義された変数のデフォルト値 |
Mappingを利用するうえで押さえておくべきポイント
Mappingは配列(Array)と非常に似ていると思うかもしれません。
Solidityの配列については下記を参照ください。
実際には配列とは大きく異なり、マッピングには長さが存在しません。
配列は、Indexの最大値を指定したり、動的配列を設定し値を設定したのちに配列の長さが設定されますが、マッピングには長さの概念がありません。
マッピングから値を取り出す場合、その対象の値のKeyを知らなければなりません。
マッピングではKeyを知っている必要があり、上記例でいえばaddressの値を知っていないと、pointsの値は取り出すことは出来ません。
配列ではそのようなことはありませんね。for文などで配列に設定されている長さを指定して、全ての値を取り出し、対象を割り出すことは可能です。
しかし、マッピングではキーを知らなければデータは取り出せません。
一見使いづらいように思いますが、Solidity,ブロックチェーンの世界ではmappingはよく利用されています。
Keyがないと値を取り出せないという特性を活かしスマートコントラクトの相手のアドレス(msg.sender)をmappingのkeyとして値を保持することに利用されているのです。
では、MappingとArray配列どちらを使うべきか?
データに対して反復処理を行う必要がある場合 (たとえば for ループ) は、配列を使用しようするべきです。
データを繰り返し処理する必要がなく、既知のキーに基づいて値を取得するのであれば、マッピングを使用するべきと言えるでしょう。
Solidity では、配列の反復処理は高コストです。あまりにも大きな配列データの反復処理をさせるとトランザクション上のETHやトークンよりもガス代が高額になってしまうこともあり得ます。
もし、Keyを設定することによってデータを管理、利用できるのであればマッピングを使うのが得策と言えるでしょう。データが大きくなりそうであればなおさらです。
マッピングであれば、スマート コントラクト内に値とキーの両方を格納する必要があるため、開発者はキーの配列を作成し、マッピング内の関連値から取得できるデータへの参照として使用することができます。
マッピングのスマートコントラクト利用例
次にSolidity でマッピングがどのように機能するかスマートコントラクトを動かしてみましょう。
このスマートコントラクトでも、addressをKeyとして、Keyの値にmsg.sender(スマートコントラクトを実行したアドレス)を設定させています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.16 <0.9.0; contract MappingExample { //addressがkey、uintがvalue、testDataは格納する値 mapping(address => uint) public testData; //keyにmsg.sender(スマコンを呼び出したアドレス)、valueに_testを紐づけてbalancesに格納する function set(uint _testData) public { testData[msg.sender] = _testData; } //msg.senderのkeyに設定された値を返すgetファンクション function get() public view returns (uint) { return testData[msg.sender]; } } |
このスマートコントラクトをDeployし、setファンクションにて10を設定して呼び出し、その後getファンクションをCallすると、下記のようにmappingの値が表示されます。
最後に
今回紹介したSolidityの仕様やコードをRemix IDEやHardhatで実際に実行やテストをして確認してみましょう。
実際に動かしたり、テストすることで理解が深まるかと思います。
hardhatのインストール手順
HardhatでスマートコントラクトのDeploy手順