いまさら、Types - Solidity（その４）

# はじめに

基本は大事ということで、Solidityを使う機会が増えてきたので、型（Types）のドキュメントを[Google翻訳](https://translate.google.co.jp/)や[DeepL翻訳](https://www.deepl.com/translator)を使用して翻訳してみます。

https://docs.soliditylang.org/en/v0.8.18/types.html

※：長いので記事を４分割します。
[その１](https://qiita.com/tnakagawa/private/f42d3936a6dda10c7b60)、[その２](https://qiita.com/tnakagawa/private/fe2480f1762cda8e6688)、[その３](https://qiita.com/tnakagawa/private/f917ed616d645cd960f0)、[その４](https://qiita.com/tnakagawa/private/cb40febf20f1742ea9bb)

※：誤字脱字、翻訳ミスなどありましたら、お知らせくださいm(__)m

## マッピング型（Mapping Types）

<details><summary>原文</summary><div>

Mapping types use the syntax `mapping(KeyType KeyName? => ValueType ValueName?)` and variables of mapping type are declared using the syntax `mapping(KeyType KeyName? => ValueType ValueName?) VariableName`.
The KeyType can be any built-in value type, `bytes`, `string`, or any contract or enum type.
Other user-defined or complex types, such as mappings, structs or array types are not allowed.
`ValueType` can be any type, including mappings, arrays and structs.
`KeyName` and `ValueName` are optional (so `mapping(KeyType => ValueType)` works as well) and can be any valid identifier that is not a type.

You can think of mappings as hash tables, which are virtually initialised such that every possible key exists and is mapped to a value whose byte-representation is all zeros, a type’s default value.
The similarity ends there, the key data is not stored in a mapping, only its `keccak256` hash is used to look up the value.

Because of this, mappings do not have a length or a concept of a key or value being set, and therefore cannot be erased without extra information regarding the assigned keys (see Clearing Mappings).

Mappings can only have a data location of `storage` and thus are allowed for state variables, as storage reference types in functions, or as parameters for library functions.
They cannot be used as parameters or return parameters of contract functions that are publicly visible.
These restrictions are also true for arrays and structs that contain mappings.

You can mark state variables of mapping type as `public` and Solidity creates a getter for you.
The `KeyType` becomes a parameter with name `KeyName` (if specified) for the getter.
If `ValueType` is a value type or a struct, the getter returns `ValueType` with name `ValueName` (if specified).
If `ValueType` is an array or a mapping, the getter has one parameter for each `KeyType`, recursively.

In the example below, the `MappingExample` contract defines a public `balances` mapping, with the key type an `address`, and a value type a `uint`, mapping an Ethereum address to an unsigned integer value.
As `uint` is a value type, the getter returns a value that matches the type, which you can see in the `MappingUser` contract that returns the value at the specified address.

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

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}

contract MappingUser {
    function f() public returns (uint) {
        MappingExample m = new MappingExample();
        m.update(100);
        return m.balances(address(this));
    }
}
```

The example below is a simplified version of an ERC20 token. 
`_allowances` is an example of a mapping type inside another mapping type.

In the example below, the optional `KeyName` and `ValueName` are provided for the mapping.
It does not affect any contract functionality or bytecode, it only sets the `name` field for the inputs and outputs in the ABI for the mapping’s getter.

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

contract MappingExampleWithNames {
    mapping(address user => uint balance) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}
```

The example below uses `_allowances` to record the amount someone else is allowed to withdraw from your account.

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

contract MappingExample {

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        require(_allowances[sender][msg.sender] >= amount, "ERC20: Allowance not high enough.");
        _allowances[sender][msg.sender] -= amount;
        _transfer(sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[sender] >= amount, "ERC20: Not enough funds.");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
    }
}
```

</div></details>

マッピング型は `mapping(KeyType KeyName? => ValueType ValueName?)` というシンタックスを使用し、マッピングタイプの変数は `mapping(KeyType KeyName? => ValueType ValueName?) VariableName` というシンタックスを使用して宣言されます。
`KeyType` には、組み込みの値型、`bytes`、`string`、あるいはコントラクト型やenum型を指定することができます。
その他のユーザー定義型や複雑な型、例えばマッピングや構造体、配列型は使用できません。
`ValueType` には、マッピング、配列、構造体を含む、任意の型を指定することができます。
`KeyName` と `ValueName` はオプションで (つまり `mapping(KeyType => ValueType)` も同様に動作します)、型ではない任意の有効な識別子を使用することができます。

マッピングはハッシュテーブルと考えることができ、仮想的にすべての可能なキーが存在し、バイト表現がすべてゼロである値、つまり型のデフォルト値にマッピングされているように初期化されます。
キーデータはマッピングに保存されず、その `keccak256` ハッシュだけが値を探すのに使われます。

このため、マッピングには長さや、キーや値が設定されているという概念がなく、割り当てられたキーに関する余分な情報がなければ消去することができません。（「マッピングの消去」を参照）

マッピングのデータロケーションは `storage` のみです。したがって、ステート変数、関数内のストレージ参照型、ライブラリ関数のパラメータとして使用することができます。
また、一般に公開されているコントラクト関数のパラメータやリターンパラメータとして使用することはできません。
これらの制限は、マッピングを含む配列や構造体にも当てはまります。

`KeyType` はゲッターのパラメータとなり、キー名 `KeyName` (指定された場合) を持ちます。
`ValueType` が値型または構造体の場合、ゲッターは `ValueType` を名前 `ValueName` (指定された場合) と共に返します。
`ValueType` が配列やマッピングの場合、ゲッターは各 `KeyType` に対して、再帰的に1つのパラメータを持つ。

以下の例では、`MappingExample`コントラクトが、キータイプを `address` 、値タイプを `uint` として、Ethereum のアドレスと符号なし整数の値を対応させた public `balances` マッピングを定義しています。
`uint` は値の型なので、ゲッターはその型に一致する値を返します。これは、指定したアドレスの値を返す `MappingUser` コントラクトで見ることができます。


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

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}

contract MappingUser {
    function f() public returns (uint) {
        MappingExample m = new MappingExample();
        m.update(100);
        return m.balances(address(this));
    }
}
```

以下の例は、ERC20 トークンの簡略化したものです。
`_allowances` は他のマッピングタイプの中にあるマッピングタイプの例です。

以下の例では、オプションの `KeyName` と `ValueName` がマッピングに提供されています。
これはコントラクトの機能やバイトコードには影響を与えず、マッピングのゲッターのABIの入力と出力に `name` フィールドを設定するだけです。

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

contract MappingExampleWithNames {
    mapping(address user => uint balance) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}
```

以下の例では、`_allowances` を使用して、誰かがあなたのアカウントから引き出すことができる金額を記録しています。

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

contract MappingExample {

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        require(_allowances[sender][msg.sender] >= amount, "ERC20: Allowance not high enough.");
        _allowances[sender][msg.sender] -= amount;
        _transfer(sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[sender] >= amount, "ERC20: Not enough funds.");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
    }
}
```

## 反復可能マッピング（Iterable Mappings）

<details><summary>原文</summary><div>

You cannot iterate over mappings, i.e. you cannot enumerate their keys.
It is possible, though, to implement a data structure on top of them and iterate over that.
For example, the code below implements an `IterableMapping` library that the `User` contract then adds data to, and the sum function iterates over to `sum` all the values.

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

struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }

struct itmap {
    mapping(uint => IndexValue) data;
    KeyFlag[] keys;
    uint size;
}

type Iterator is uint;

library IterableMapping {
    function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
        uint keyIndex = self.data[key].keyIndex;
        self.data[key].value = value;
        if (keyIndex > 0)
            return true;
        else {
            keyIndex = self.keys.length;
            self.keys.push();
            self.data[key].keyIndex = keyIndex + 1;
            self.keys[keyIndex].key = key;
            self.size++;
            return false;
        }
    }

    function remove(itmap storage self, uint key) internal returns (bool success) {
        uint keyIndex = self.data[key].keyIndex;
        if (keyIndex == 0)
            return false;
        delete self.data[key];
        self.keys[keyIndex - 1].deleted = true;
        self.size --;
    }

    function contains(itmap storage self, uint key) internal view returns (bool) {
        return self.data[key].keyIndex > 0;
    }

    function iterateStart(itmap storage self) internal view returns (Iterator) {
        return iteratorSkipDeleted(self, 0);
    }

    function iterateValid(itmap storage self, Iterator iterator) internal view returns (bool) {
        return Iterator.unwrap(iterator) < self.keys.length;
    }

    function iterateNext(itmap storage self, Iterator iterator) internal view returns (Iterator) {
        return iteratorSkipDeleted(self, Iterator.unwrap(iterator) + 1);
    }

    function iterateGet(itmap storage self, Iterator iterator) internal view returns (uint key, uint value) {
        uint keyIndex = Iterator.unwrap(iterator);
        key = self.keys[keyIndex].key;
        value = self.data[key].value;
    }

    function iteratorSkipDeleted(itmap storage self, uint keyIndex) private view returns (Iterator) {
        while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
            keyIndex++;
        return Iterator.wrap(keyIndex);
    }
}

// How to use it
contract User {
    // Just a struct holding our data.
    itmap data;
    // Apply library functions to the data type.
    using IterableMapping for itmap;

    // Insert something
    function insert(uint k, uint v) public returns (uint size) {
        // This calls IterableMapping.insert(data, k, v)
        data.insert(k, v);
        // We can still access members of the struct,
        // but we should take care not to mess with them.
        return data.size;
    }

    // Computes the sum of all stored data.
    function sum() public view returns (uint s) {
        for (
            Iterator i = data.iterateStart();
            data.iterateValid(i);
            i = data.iterateNext(i)
        ) {
            (, uint value) = data.iterateGet(i);
            s += value;
        }
    }
}
```

</div></details>

マッピングを反復することはできません。つまり、マッピングのキーを列挙することはできません。
しかし、マッピングの上にデータ構造を実装して、それを反復処理することは可能です。
例えば、以下のコードでは `IterableMapping` ライブラリを実装しており、 `User` 契約がデータを追加し、`sum` 関数がすべての値を反復処理するようになっています。


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

struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }

struct itmap {
    mapping(uint => IndexValue) data;
    KeyFlag[] keys;
    uint size;
}

type Iterator is uint;

library IterableMapping {
    function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
        uint keyIndex = self.data[key].keyIndex;
        self.data[key].value = value;
        if (keyIndex > 0)
            return true;
        else {
            keyIndex = self.keys.length;
            self.keys.push();
            self.data[key].keyIndex = keyIndex + 1;
            self.keys[keyIndex].key = key;
            self.size++;
            return false;
        }
    }

    function remove(itmap storage self, uint key) internal returns (bool success) {
        uint keyIndex = self.data[key].keyIndex;
        if (keyIndex == 0)
            return false;
        delete self.data[key];
        self.keys[keyIndex - 1].deleted = true;
        self.size --;
    }

    function contains(itmap storage self, uint key) internal view returns (bool) {
        return self.data[key].keyIndex > 0;
    }

    function iterateStart(itmap storage self) internal view returns (Iterator) {
        return iteratorSkipDeleted(self, 0);
    }

    function iterateValid(itmap storage self, Iterator iterator) internal view returns (bool) {
        return Iterator.unwrap(iterator) < self.keys.length;
    }

    function iterateNext(itmap storage self, Iterator iterator) internal view returns (Iterator) {
        return iteratorSkipDeleted(self, Iterator.unwrap(iterator) + 1);
    }

    function iterateGet(itmap storage self, Iterator iterator) internal view returns (uint key, uint value) {
        uint keyIndex = Iterator.unwrap(iterator);
        key = self.keys[keyIndex].key;
        value = self.data[key].value;
    }

    function iteratorSkipDeleted(itmap storage self, uint keyIndex) private view returns (Iterator) {
        while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
            keyIndex++;
        return Iterator.wrap(keyIndex);
    }
}

// How to use it
contract User {
    // Just a struct holding our data.
    itmap data;
    // Apply library functions to the data type.
    using IterableMapping for itmap;

    // Insert something
    function insert(uint k, uint v) public returns (uint size) {
        // This calls IterableMapping.insert(data, k, v)
        data.insert(k, v);
        // We can still access members of the struct,
        // but we should take care not to mess with them.
        return data.size;
    }

    // Computes the sum of all stored data.
    function sum() public view returns (uint s) {
        for (
            Iterator i = data.iterateStart();
            data.iterateValid(i);
            i = data.iterateNext(i)
        ) {
            (, uint value) = data.iterateGet(i);
            s += value;
        }
    }
}
```

## 演算子（Operators）

<details><summary>原文</summary><div>

Arithmetic and bit operators can be applied even if the two operands do not have the same type.
For example, you can compute `y = x + z`, where `x` is a `uint8` and `z` has the type `uint32`.
In these cases, the following mechanism will be used to determine the type in which the operation is computed (this is important in case of overflow) and the type of the operator’s result:

1. If the type of the right operand can be implicitly converted to the type of the left operand, use the type of the left operand,
1. if the type of the left operand can be implicitly converted to the type of the right operand, use the type of the right operand,
1. otherwise, the operation is not allowed.

In case one of the operands is a literal number it is first converted to its “mobile type”, which is the smallest type that can hold the value (unsigned types of the same bit-width are considered “smaller” than the signed types).
If both are literal numbers, the operation is computed with effectively unlimited precision in that the expression is evaluated to whatever precision is necessary so that none is lost when the result is used with a non-literal type.

The operator’s result type is the same as the type the operation is performed in, except for comparison operators where the result is always `bool`.

The operators `**` (exponentiation), `<<` and `>>` use the type of the left operand for the operation and the result.

</div></details>

算術演算子やビット演算子は、2つのオペランドの型が同じでなくても適用することができます。
例えば、 `y = x + z` という計算ができますが、ここで `x` は `uint8` で、 `z` は `uint32` という型になります。
このような場合、以下のメカニズムにより、演算が行われた型（これはオーバーフローの場合に重要です）と演算子の結果の型が決定されます。

1. 右オペランドの型を左オペランドの型に暗黙のうちに変換できる場合は、左オペランドの型を使用します。
1. 左オペランドの型を右オペランドの型に暗黙のうちに変換できる場合は、右オペランドの型を使用します。
1. それ以外の場合は、操作は許可されません。

オペランドの一方がリテラル数値の場合、まずその値を保持できる最小の型である「モバイル型」に変換されます（同じビット幅の符号なし型は、符号付き型よりも「小さい」とみなされます）。
両方ともリテラル数である場合、演算は事実上無制限の精度で計算されます。つまり、式は必要な精度で評価され、結果が非リテラル型と一緒に使われたときに何も失われません。

演算子の結果の型は、結果が常に `bool` になる比較演算子を除いて、演算が行われた型と同じになります。

演算子 `**` (指数)、`<<` および `>>` は、左側のオペランドの型を使って演算と結果を行います。

## 三項演算子（Ternary Operator）

<details><summary>原文</summary><div>

The ternary operator is used in expressions of the form `<expression> ? <trueExpression> : <falseExpression>`.
It evaluates one of the latter two given expressions depending upon the result of the evaluation of the main `<expression>`.
If `<expression>` evaluates to true, then `<trueExpression>` will be evaluated, otherwise `<falseExpression>` is evaluated.

The result of the ternary operator does not have a rational number type, even if all of its operands are rational number literals. 
The result type is determined from the types of the two operands in the same way as above, converting to their mobile type first if required.

As a consequence, `255 + (true ? 1 : 0)` will revert due to arithmetic overflow.
The reason is that `(true ? 1 : 0)` is of `uint8` type, which forces the addition to be performed in `uint8` as well, and 256 exceeds the range allowed for this type.

Another consequence is that an expression like `1.5 + 1.5` is valid but `1.5 + (true ? 1.5 : 2.5)` is not.
This is because the former is a rational expression evaluated in unlimited precision and only its final value matters.
The latter involves a conversion of a fractional rational number to an integer, which is currently disallowed.

</div></details>

三項演算子は、`<expression> ? <trueExpression> : <falseExpression>`のような形の式で使われます。
これは、メインの `<expression>` の評価結果に応じて、後者の2つの式のうちどちらかを評価します。
もし `<expression>` が真と評価されれば `<trueExpression>` が評価され、そうでなければ `<falseExpression>` が評価されます。

三項演算子の結果は、たとえオペランドがすべて有理数リテラルであっても有理数型を持ちません。
結果の型は、上記と同じように2つのオペランドの型から決定され、必要であれば最初にそのモバイル型に変換されます。

その結果、`255 + (true ? 1 : 0)` は算術オーバーフローにより戻されます。
これは、`(true ? 1 : 0)` が `uint8` 型であり、足し算も `uint8` で行わなければならず、256 がこの型で許される範囲を超えているからです。

もう一つの結果は、`1.5 + 1.5` のような式は有効ですが、`1.5 + (true ? 1.5 : 2.5)` は無効であるということです。
これは、前者が無制限の精度で評価される有理数式であり、その最終値のみが重要だからです。
後者は分数の有理数から整数への変換を伴いますが、これは現在許可されていません。

## 複合演算子、インクリメント／デクリメント演算子（Compound and Increment/Decrement Operators）

<details><summary>原文</summary><div>

If `a` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands:

`a += e` is equivalent to `a = a + e`.
The operators `-=`, `*=`, `/=`, `%=`, `|=`, `&=`, `^=`, `<<=` and `>>=` are defined accordingly.
`a++` and `a--` are equivalent to `a += 1` / `a -= 1` but the expression itself still has the previous value of `a`.
In contrast, `--a` and `++a` have the same effect on `a` but return the value after the change.

</div></details>

もし `a` が LValue (つまり変数や代入できるもの) であれば、以下の演算子が短縮形として利用できます。

`a += e` は `a = a + e` と同じです。
演算子 `-=`, `*=`, `/=`, `%=`, `|=`, `&=`, `^=`, `<<=`, `>>=` は適宜定義されています。
`a++` と `a--` は `a += 1` / `a -= 1` と同じですが、式自体は以前の値である `a` を保持したままです。
これに対して、`--a` と `++a` は `a` に対して同じ効果を与えますが、変更後の値を返します。

## 削除（delete）

<details><summary>原文</summary><div>

`delete a` assigns the initial value for the type to `a`.
I.e. for integers it is equivalent to `a = 0`, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements set to their initial value.
`delete a[x]` deletes the item at index `x` of the array and leaves all other elements and the length of the array untouched.
This especially means that it leaves a gap in the array.
If you plan to remove items, a mapping is probably a better choice.

For structs, it assigns a struct with all members reset.
In other words, the value of `a` after `delete a` is the same as if `a` would be declared without assignment, with the following caveat:

`delete` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown).
So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings.
However, individual keys and what they map to can be deleted: If `a` is a mapping, then `delete a[x]` will delete the value stored at `x`.

It is important to note that `delete a` really behaves like an assignment to `a`, i.e. it stores a new object in `a`.
This distinction is visible when `a` is reference variable: It will only reset `a` itself, not the value it referred to previously.

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

contract DeleteExample {
    uint data;
    uint[] dataArray;

    function f() public {
        uint x = data;
        delete x; // sets x to 0, does not affect data
        delete data; // sets data to 0, does not affect x
        uint[] storage y = dataArray;
        delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
        // y is affected which is an alias to the storage object
        // On the other hand: "delete y" is not valid, as assignments to local variables
        // referencing storage objects can only be made from existing storage objects.
        assert(y.length == 0);
    }
}
```

</div></details>

`delete a` は `a` にその型の初期値を代入します。
つまり，整数の場合は `a = 0` と同じですが，配列に対しても使用することができます。この場合，長さ 0 の動的配列，あるいは同じ長さですべての要素が初期値に設定された静的配列を代入します。
`delete a[x]` は配列のインデックス `x` の項目を削除し、他のすべての要素と配列の長さをそのまま残します。
これは特に、配列に隙間を残すことを意味します。
もしアイテムを削除する予定があるなら、マッピングの方が良い選択でしょう。

構造体の場合は、すべてのメンバがリセットされた構造体が代入されます。
つまり、 `delete a` の後の `a` の値は、以下の注意点を除いて、 `a` を割り当てずに宣言した場合と同じになります。

`delete` はマッピングには効果がありません（マッピングのキーは任意であり、一般に未知であるため）。
そのため、構造体を削除すると、マッピングでないメンバはすべてリセットされ、マッピングでないメンバにも再帰的にアクセスすることになります。
しかし、個々のキーとそのマッピングは削除することができます。`a` がマッピングの場合、 `delete a[x]` は `x` に格納されている値を削除します。

重要なことは、 `delete a` は実際には `a` への代入のように動作することです。つまり、新しいオブジェクトを `a` に格納します。
この区別は `a` が参照変数の場合に見ることができます。これは `a` 自身をリセットするだけで、前に参照していた値をリセットするわけではありません。

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

contract DeleteExample {
    uint data;
    uint[] dataArray;

    function f() public {
        uint x = data;
        delete x; // sets x to 0, does not affect data
        delete data; // sets data to 0, does not affect x
        uint[] storage y = dataArray;
        delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
        // y is affected which is an alias to the storage object
        // On the other hand: "delete y" is not valid, as assignments to local variables
        // referencing storage objects can only be made from existing storage objects.
        assert(y.length == 0);
    }
}
```

## 演算子の優先順（Order of Precedence of Operators）

<details><summary>原文</summary><div>

The following is the order of precedence for operators, listed in order of evaluation.

|Precedence|Description|Operator|
|:--|:--|:--|
|1|Postfix increment and decrement|`++`, `--`|
||New expression|`new <typename>`|
||Array subscripting|`<array>[<index>]`|
||Member access|`<object>.<member>`|
||Function-like call|`<func>(<args...>)`|
||Parentheses|`(<statement>)`|
|2|Prefix increment and decrement|`++`, `--`|
||Unary minus|`-`|
||Unary operations|`delete`|
||Logical NOT|`!`|
||Bitwise NOT|`~`|
|3|Exponentiation|`**`|
|4|Multiplication, division and modulo|`*`, `/`, `%`|
|5|Addition and subtraction|`+`, `-`|
|6|Bitwise shift operators|`<<`, `>>`|
|7|Bitwise AND|`&`|
|8|Bitwise XOR|`^`|
|9|Bitwise OR|`|`|
|10|Inequality operators|`<`, `>`, `<=`, `>=`|
|11|Equality operators|`==`, `!=`|
|12|Logical AND|`&&`|
|13|Logical OR|`||`|
|14|Ternary operator|`<conditional> ? <if-true> : <if-false>`|
||Assignment operators|`=`, `\\|=`, `^=`, `&=`, `<<=`, `>>=`, `+=`, `-=`, `*=`, `/=`, `%=`|
|15|Comma operator|`,`|

</div></details>

演算子の優先順位を評価順に並べると次のようになります。

|優先順位|説明|演算子|
|:--|:--|:--|
|1|接尾インクリメントとデクリメント|`++`, `--`|
||New 式|`new <typename>`|
||配列の添字|`<array>[<index>]`|
||メンバアクセス|`<object>.<member>`|
||関数的な呼び出し|`<func>(<args...>)`|
||括弧内|`(<statement>)`|
|2|接頭インクリメントとデクリメント|`++`, `--`|
||単項マイナス|`-`|
||単項演算|`delete`|
||論理NOT|`!`|
||ビット単位NOT|`~`|
|3|べき乗|`**`|
|4|乗算、除算、モジュロ|`*`, `/`, `%`|
|5|加算、減算|`+`, `-`|
|6|ビット単位シフト演算|`<<`, `>>`|
|7|ビット単位AND|`&`|
|8|ビット単位XOR|`^`|
|9|ビット単位OR|`|`|
|10|不等号演算|`<`, `>`, `<=`, `>=`|
|11|等号演算|`==`, `!=`|
|12|論理AND|`&&`|
|13|論理OR|`||`|
|14|三項演算子|`<conditional> ? <if-true> : <if-false>`|
||代入演算|`=`, `\\|=`, `^=`, `&=`, `<<=`, `>>=`, `+=`, `-=`, `*=`, `/=`, `%=`|
|15|カンマ演算|`,`|

## 基本型間の変換（Conversions between Elementary Types）

### 暗黙の変換（Implicit Conversions）

<details><summary>原文</summary><div>

An implicit type conversion is automatically applied by the compiler in some cases during assignments, when passing arguments to functions and when applying operators.
In general, an implicit conversion between value-types is possible if it makes sense semantically and no information is lost.

For example, `uint8` is convertible to `uint16` and `int128` to `int256`, but `int8` is not convertible to `uint256`, because `uint256` cannot hold values such as `-1`.

If an operator is applied to different types, the compiler tries to implicitly convert one of the operands to the type of the other (the same is true for assignments).
This means that operations are always performed in the type of one of the operands.

For more details about which implicit conversions are possible, please consult the sections about the types themselves.

In the example below, `y` and `z`, the operands of the addition, do not have the same type, but `uint8` can be implicitly converted to `uint16` and not vice-versa.
Because of that, `y` is converted to the type of `z` before the addition is performed in the `uint16` type.
The resulting type of the expression `y + z` is `uint16`.
Because it is assigned to a variable of type `uint32` another implicit conversion is performed after the addition.

```solidity
uint8 y;
uint16 z;
uint32 x = y + z;
```

</div></details>

暗黙の型変換は、代入時、関数への引数渡し時、演算子適用時にコンパイラによって自動的に適用される場合があります。
一般に、値型間の暗黙の変換は、それが意味的に意味を持ち、情報が失われない場合には可能です。

例えば、 `uint8` は `uint16` に、 `int128` は `int256` に変換できますが、 `uint256` は `-1` などの値を保持できないため、 `int8` は `uint256` に変換できません。

演算子が異なる型に適用される場合、コンパイラはオペランドの一方を暗黙のうちに他方の型に変換しようとします。（代入の場合も同様）
つまり、演算は常に一方のオペランドの型で実行されます。

どのような暗黙の変換が可能かについての詳細は、型そのものについてのセクションを参照してください。

下の例では、加算のオペランドである `y` と `z` は同じ型ではありませんが、 `uint8` は暗黙のうちに `uint16` に変換でき、その逆はできません。
そのため、`y` は `uint16` 型で加算を行う前に `z` 型に変換されます。
その結果、式 `y + z` の型は `uint16` となる。
これは `uint32` 型の変数に代入されるため、加算の後に別の暗黙の変換が行われます。

```solidity
uint8 y;
uint16 z;
uint32 x = y + z;
```

### 明示的な変換（Explicit Conversions）

<details><summary>原文</summary><div>

If the compiler does not allow implicit conversion but you are confident a conversion will work, an explicit type conversion is sometimes possible.
This may result in unexpected behaviour and allows you to bypass some security features of the compiler, so be sure to test that the result is what you want and expect!

Take the following example that converts a negative `int` to a `uint`:

```solidity
int  y = -3;
uint x = uint(y);
```

At the end of this code snippet, `x` will have the value `0xfffff..fd` (64 hex characters), which is -3 in the two’s complement representation of 256 bits.

If an integer is explicitly converted to a smaller type, higher-order bits are cut off:

```solidity
uint32 a = 0x12345678;
uint16 b = uint16(a); // b will be 0x5678 now
```

If an integer is explicitly converted to a larger type, it is padded on the left (i.e., at the higher order end).
The result of the conversion will compare equal to the original integer:

```solidity
uint16 a = 0x1234;
uint32 b = uint32(a); // b will be 0x00001234 now
assert(a == b);
```

Fixed-size bytes types behave differently during conversions.
They can be thought of as sequences of individual bytes and converting to a smaller type will cut off the sequence:

```solidity
bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b will be 0x12
```

If a fixed-size bytes type is explicitly converted to a larger type, it is padded on the right.
Accessing the byte at a fixed index will result in the same value before and after the conversion (if the index is still in range):

```solidity
bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b will be 0x12340000
assert(a[0] == b[0]);
assert(a[1] == b[1]);
```

Since integers and fixed-size byte arrays behave differently when truncating or padding, explicit conversions between integers and fixed-size byte arrays are only allowed, if both have the same size.
If you want to convert between integers and fixed-size byte arrays of different size, you have to use intermediate conversions that make the desired truncation and padding rules explicit:


```solidity
bytes2 a = 0x1234;
uint32 b = uint16(a); // b will be 0x00001234
uint32 c = uint32(bytes4(a)); // c will be 0x12340000
uint8 d = uint8(uint16(a)); // d will be 0x34
uint8 e = uint8(bytes1(a)); // e will be 0x12
```

`bytes` arrays and `bytes` calldata slices can be converted explicitly to fixed bytes types (`bytes1`/…/`bytes32`).
In case the array is longer than the target fixed bytes type, truncation at the end will happen. If the array is shorter than the target type, it will be padded with zeros at the end.

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

contract C {
    bytes s = "abcdefgh";
    function f(bytes calldata c, bytes memory m) public view returns (bytes16, bytes3) {
        require(c.length == 16, "");
        bytes16 b = bytes16(m);  // if length of m is greater than 16, truncation will happen
        b = bytes16(s);  // padded on the right, so result is "abcdefgh\0\0\0\0\0\0\0\0"
        bytes3 b1 = bytes3(s); // truncated, b1 equals to "abc"
        b = bytes16(c[:8]);  // also padded with zeros
        return (b, b1);
    }
}
```

</div></details>

コンパイラが暗黙的な変換を許可していなくても、変換が機能すると確信している場合は、明示的な型変換が可能な場合があります。
これにより、予期しない動作が発生する可能性があり、コンパイラの一部のセキュリティ機能をバイパスできます。そのため、結果が意図したとおりであることを必ずテストしてください！

次の例では、負の `int` を `uint` に変換しています。

```solidity
int  y = -3;
uint x = uint(y);
```

このコードの最後では、`x`は`0xfffff..fd`（64文字の16進数）という値を持ち、これは256ビットの2の補数表現で-3です。

整数をより小さい型に明示的に変換する場合，高次のビットはカットされます。

```solidity
uint32 a = 0x12345678;
uint16 b = uint16(a); // b will be 0x5678 now
```

整数がより大きな型に明示的に変換された場合，左側（つまり高次の端）にパディングされます。
変換の結果は元の整数と比較されます。

```solidity
uint16 a = 0x1234;
uint32 b = uint32(a); // b will be 0x00001234 now
assert(a == b);
```

固定サイズのバイトタイプは、変換時に異なる動作をします。
それらは個々のバイトのシーケンスと考えることができ、より小さなタイプに変換するとシーケンスが切断されます。

```solidity
bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b will be 0x12
```

固定サイズのバイト型がより大きな型に明示的に変換された場合、右側にパディングされます。
固定インデックスのバイトにアクセスすると、変換前と変換後では同じ値になります。（インデックスがまだ範囲内にある場合）

```solidity
bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b will be 0x12340000
assert(a[0] == b[0]);
assert(a[1] == b[1]);
```

整数と固定サイズのバイト配列は、切り捨てやパディングの際に異なる振る舞いをするので、整数と固定サイズのバイト配列の間の明示的な変換は、両者が同じサイズである場合にのみ許可されます。
もし、異なるサイズの整数と固定サイズバイト配列の間を変換したいならば、 望みの切り捨てとパディングの規則を明示的にする中間変換を使わなければなりません。

```solidity
bytes2 a = 0x1234;
uint32 b = uint16(a); // b will be 0x00001234
uint32 c = uint32(bytes4(a)); // c will be 0x12340000
uint8 d = uint8(uint16(a)); // d will be 0x34
uint8 e = uint8(bytes1(a)); // e will be 0x12
```

`bytes` 配列と `bytes` calldata スライスは、明示的に固定バイト型 (`bytes1`/.../`bytes32`) に変換することが可能です。
配列が目的の固定バイト型よりも長い場合は、末尾で切り詰めます。配列が目的の固定バイト型よりも短い場合は、末尾に 0 が詰められます。

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

contract C {
    bytes s = "abcdefgh";
    function f(bytes calldata c, bytes memory m) public view returns (bytes16, bytes3) {
        require(c.length == 16, "");
        bytes16 b = bytes16(m);  // if length of m is greater than 16, truncation will happen
        b = bytes16(s);  // padded on the right, so result is "abcdefgh\0\0\0\0\0\0\0\0"
        bytes3 b1 = bytes3(s); // truncated, b1 equals to "abc"
        b = bytes16(c[:8]);  // also padded with zeros
        return (b, b1);
    }
}
```

## リテラルと初等型の間の変換（Conversions between Literals and Elementary Types）

### 整数型（Integer Types）

<details><summary>原文</summary><div>

Decimal and hexadecimal number literals can be implicitly converted to any integer type that is large enough to represent it without truncation:

```solidity
uint8 a = 12; // fine
uint32 b = 1234; // fine
uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456
```

:::note
Note
Prior to version 0.8.0, any decimal or hexadecimal number literals could be explicitly converted to an integer type.
From 0.8.0, such explicit conversions are as strict as implicit conversions, i.e., they are only allowed if the literal fits in the resulting range.
:::

</div></details>

10進数と16進数の数値リテラルは、切り捨てずに表現するのに十分な大きさの任意の整数型に暗黙のうちに変換することができます。

```solidity
uint8 a = 12; // fine
uint32 b = 1234; // fine
uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456
```

:::note
Note
バージョン0.8.0以前は、10進数や16進数のリテラルはすべて明示的に整数型に変換することができました。
0.8.0以降では、このような明示的な変換は暗黙的な変換と同様に厳格で、つまり、リテラルが結果の範囲に収まっている場合にのみ許可されます。
:::

### 固定サイズバイト配列（Fixed-Size Byte Arrays）

<details><summary>原文</summary><div>

Decimal number literals cannot be implicitly converted to fixed-size byte arrays.
Hexadecimal number literals can be, but only if the number of hex digits exactly fits the size of the bytes type.
As an exception both decimal and hexadecimal literals which have a value of zero can be converted to any fixed-size bytes type:

```solidity
bytes2 a = 54321; // not allowed
bytes2 b = 0x12; // not allowed
bytes2 c = 0x123; // not allowed
bytes2 d = 0x1234; // fine
bytes2 e = 0x0012; // fine
bytes4 f = 0; // fine
bytes4 g = 0x0; // fine
```

String literals and hex string literals can be implicitly converted to fixed-size byte arrays, if their number of characters matches the size of the bytes type:

```solidity
bytes2 a = hex"1234"; // fine
bytes2 b = "xy"; // fine
bytes2 c = hex"12"; // not allowed
bytes2 d = hex"123"; // not allowed
bytes2 e = "x"; // not allowed
bytes2 f = "xyz"; // not allowed
```

</div></details>

10進数リテラルは、固定サイズのバイト配列に暗黙のうちに変換することはできません。
16進数リテラルは変換可能ですが、16進数の桁数がバイト型のサイズにぴったり合う場合のみです。
例外として、値が0の10進数および16進数リテラルは、任意の固定サイズバイト型に変換することができます。

```solidity
bytes2 a = 54321; // not allowed
bytes2 b = 0x12; // not allowed
bytes2 c = 0x123; // not allowed
bytes2 d = 0x1234; // fine
bytes2 e = 0x0012; // fine
bytes4 f = 0; // fine
bytes4 g = 0x0; // fine
```

文字列リテラルと16進文字列リテラルは、その文字数がbytes型のサイズと一致する場合、暗黙のうちに固定サイズのバイト配列に変換されることがあります。

```solidity
bytes2 a = hex"1234"; // fine
bytes2 b = "xy"; // fine
bytes2 c = hex"12"; // not allowed
bytes2 d = hex"123"; // not allowed
bytes2 e = "x"; // not allowed
bytes2 f = "xyz"; // not allowed
```

## アドレス（Addresses）

<details><summary>原文</summary><div>

As described in Address Literals, hex literals of the correct size that pass the checksum test are of `address` type.
No other literals can be implicitly converted to the `address` type.

Explicit conversions to `address` are allowed only from `bytes20` and `uint160`.

An `address a` can be converted explicitly to `address payable` via `payable(a)`.

:::note
Note
Prior to version 0.8.0, it was possible to explicitly convert from any integer type (of any size, signed or unsigned) to `address` or `address payable`.
Starting with in 0.8.0 only conversion from `uint160` is allowed.
:::

</div></details>

アドレス・リテラルで説明したように、チェックサムテストをパスした正しいサイズの16進数リテラルは `address` 型になります。
他のリテラルは暗黙のうちに `address` 型に変換されることはありません。

アドレスへの明示的な変換は、`bytes20` と `uint160` からのみ可能です。

`address a` は `payable(a)` を介して明示的に `address payable` に変換することができます。

:::note
Note
バージョン 0.8.0 以前では、任意の整数型 (サイズ、符号付き、符号なし) から `address` や `address payable` に明示的に変換することが可能でした。
0.8.0 以降では、 `uint160` からの変換のみが可能です。
:::

# さいごに

ここで注目したのは、マッピング型です。
`KeyName` が存在しない（設定していない）場合は、`ValueName` は初期値として返ってくるみたいです。
注意しなければならないと思いました。

一旦、ざっとみたところ、JavaScriptに似ていますが、異なる部分があるので慎重に設計・製造しなければならなそうです。