今回は、Solidityでスマートコントラクトを書く際に欠かせないmappingに重複したキーで値を代入しようとした時の挙動について解説していきます。
mappingに重複キーで値を代入した場合の挙動
結論、mapping型の変数に重複したキーで値を代入しようとした場合、新しく代入しようとしている値とキーがマッピングされ、mappingのキー・バリューが更新されます。
例えば、以下のような場合は20がtestMappingの返却値となります。
contract TestMapping {
mapping(uint => uint) public testMapping;
function testMapping() public returns (uint) {
testMapping[1] = 10;
testMapping[1] = 20;
return testMapping[1]; // 20が返却される
}
}
mappingを使う際に気をつけること
上記で説明したように、重複したキーでmappingに代入しようとするとキーに対応する値が上書きされてしまいます。
この挙動は、先ほどの例の様に単純なものだとそこまで気にすることはないですが、気づかないうちに重複したキーで値をmappingに代入してしまい、思わぬバグを生み出しかねないので注意が必要です。
例えば、ランダムで生成したユニークIDをキーとしてmappingに値を格納しており、たまたまランダムで生成されたIDが既存のものと被ってしまっていて知らない間に以前の値が消えていた、なんてことも起こり得ます。
そうならないためにも、キーの値がかぶる可能性がある場合は、キーが既に存在しないかを確認してから値を代入するようにした方が良さそうです。
重複するキーがないか確認する方法
mapping内にあるキーが存在するかしないかは以下のようにして確認できます。
// mapping(uint256 => address) ownerOf; の場合
// キー「1」に対応したaddressがないか(=trueが返却されれば、キーが存在する)
ownerOf(1) != address(0)
// キー「1」に対応したaddressが初期値か(=trueが返却されれば、キーは存在しない)
ownerOf(1) == address(0)
Solidityではmappingで指定したキーに対応するバリューがない場合は、バリュー側には指定した型の初期値が返却されます。
なので、uint256型のキーが存在するかどうかは、キーに対応するaddress型のバリューがゼロアドレスかどうかを見ることで確認できます。
ただし、上記の場合は、キーに対してゼロアドレスが設定されていないことが前提となります。
struct型のバリューの場合
mappingのバリューのデータ型が単純にaddressやuint256などの場合は、簡単ですが、struct型で定義されている場合は少しだけ複雑になります。
struct型のバリューの場合は以下の様に確認できます。
contract Test {
uint256 private _id;
struct TestStruct {
uint256 id;
string name;
}
mapping(uint256 => TestStruct) public testMapping;
function keyExists(uint256 key) internal returns(bool) {
return testMapping[key].id != 0; // trueが返却されればキーに対応する値が存在している
}
}
struct型のバリュー場合も、struct内の特定のフィールドの初期値かどうかを確認することでそのキーに対応するバリューが存在しているかを確認できます。
1点注意としては、初期値が入る可能性のあるフィールドで確認しないことです。
それをしてしまうと、キーに対応するバリューが代入されているのにないものとして判断されてしかねないため、必ず値が入っているフィールドでキーが重複していないかの確認をするようにしましょう。
コメント