Now and Nawoo

NFTの制作記録、技術メモ → C#, Solidity, Blockchain, Bitcoin, Ethereum, NFT

Solidityメモ: 型変換(キャスト)

Solidityの型変換(キャスト)は少しややこしいので、整理してみました。

uintとint

範囲外であってもエラーにならずに変換されます。(例えば intの-1はuintの255になります。)

// int→uint (uintの範囲内)
int8 i1 = 127;
uint8 u1 = uint8(i1); // 127

// int→uint (uintの範囲外)
int8 i2 = -1;
uint8 u2 = uint8(i2); // 255

// uint→int (intの範囲内)
uint8 u3 = 10;
int8 i3 = int8(u3); // 10

// uint→int (intの範囲外)
uint8 u4 = 200;
int8 i4 = int8(u4); // -56

bytesとstring

bytesとstringは相互に変換可能です。

// string → bytes
string memory s ="hello";
bytes memory b = bytes(s); 

// bytes → string
bytes memory b = "world";
string memory s = string(b); 

stringの長さを取得するときは、いったんbytesに変換してからlengthを使ったりします。(stringではlengthが使えないため)

string memory s = "hello";
uint len = bytes(s).length;

小さな型/大きな型への変換

uint16→uint8 のように同じuintで小さな型に変換する場合や、uint16→uint32のように大きな型に変換する場合は注意が必要です。

uintとbytesNでは挙動が違います。

// uintの場合
uint16 a = 0xabcd;
uint8 b  = uint8(a);  // 0xcdになる (上位ビットが切り捨てられる)
uint32 c = uint32(a); // 0x0000abcd (左がゼロで埋められる)

// bytesNの場合
bytes2 a = 0xabcd;
bytes1 b = bytes1(a); // 0xabになる (下位ビットが切り捨てられる)
bytes4 c = bytes4(a); // 0xabcd0000 (右がゼロで埋められる)

これが原因で、キャスト途中の型によって結果が変わることがあります。 例えば、uint8からbytes4に変換する場合では以下のようになります。

uint8 a = 0xab;
bytes4 b1 = bytes4(bytes1(a)); // bytes1を経由して変換すると結果は 0xab000000
bytes4 b2 = bytes4(uint32(a)); // uint32を経由して変換すると結果は 0x000000ab

intの場合

intもuintと同じように小さな型への変換では上位バイトが切り捨てられ、大きな型への変換では左がゼロで埋められます。

int16 a = -12345; // 0xcfc7
int8 b = int8(a); // -57 = 0xc7
int32 c = int32(a); // -12345 = 0x0000cfc7

bytesNとbytes

bytesからbytesNはキャストできます。挙動もbytesN同士のキャストと同じです。 (古い記事では1バイトずつコピーしている例がありますが、0.8.5からできるようになりました。)

bytes memory b = hex"123456";
bytes2 b2 = bytes2(b); // 0x1234 (下位ビットが切り捨て)
bytes4 b4 = bytes4(b); // 0x12345600 (右がゼロで埋められる)

bytesNからbytesへのキャストはできないようなので、abi.encodePackedを使いましょう。

bytes2 a = 0xabcd;
bytes memory b = abi.encodePacked(a); // 0xabcd