変数・定数・Collection の全体像
変数・定数
宣言方法
letは定数を、varは変数を表します。
pub fun main {
var a: Int = 2 // 通常の型はnil(ヌル)を含むことができません。
var a: Int? = nil // 変数にnilを入れられるようにするには、Optional Typeを使用します。
var b: Int = a! // Optional Typeをから通常の変数に変換するには ! を使用します。(この場合はnilなのでエラーになります)
}
Cadenceでは、Optional Typeから通常のTypeに変換することがよくあります。そのような場合、Optional Binding ( =if let文 )を使って実装すると、実装が簡単になります。(if letの中に入る変数はOptionalではなくなります)
型の種類
Boolean型
let boolVar: Bool = true
Int型
let int8: Int8 = 127 // -128~127
let int16: Int16 = 32767 // -32768~32767
let int32: Int32 = 2147483647 // -2147483648~2147483647
let int64: Int64 = 9223372036854775807 // -9223372036854775808~9223372036854775807
let int128: Int128 = 9223372036854775808 // -2^127~2^127 − 1
let int256: Int256 = 9223372036854775808 // -2^255~2^255 − 1
UInt型
let uint8: UInt8 = 255 // 0~255
let uint16: UInt16 = 65535 // 0~65535
let uint32: UInt32 = 4294967295 // 0~4294967295
let uint64: UInt64 = 18446744073709551615 // 0~18446744073709551615
let uint128: UInt128 = 18446744073709551616 // 0~2^128 − 1
let uint256: UInt256 = 18446744073709551616 // 0~2^256 − 1
任意精度型 (Int/UInt)
let integer: Int = 1 // 任意精度(理論上無制限 / メモリ依存) / Arbitrary-precision (unbounded)
let uinteger: UInt = 1 // 任意精度(理論上無制限 / メモリ依存) / Arbitrary-precision (unbounded)
Word型
オーバーフロー,アンダーフローのチェックを行わない,すなわちラップアラウンドする符号なし整数型は,Wordの接頭辞を持ち,以下の範囲の値を表現することができます。
let nilWord: Word8? = Word8.fromString("1024") // nil, out of bounds, 0~255
let word16: Word16? = Word16.fromString("255") // 0~65535
let word32: Word32? = Word32.fromString("255") // 0~4294967295
let word64: Word64? = Word64.fromString("255") // 0~18446744073709551615
Fix / UFix型
let fix64: Fix64? = Fix64.fromString("-0.1") // ok, -92233720368.54775808~92233720368.54775807
let ufix64: UFix64? = UFix64.fromString("-0.1") // nil, 0.0~184467440737.09551615
Address型
let some: Address = 0x436164656E636521 // UInt64 length. (Max:18446744073709551615)
AnyStruct / AnyResource型
全ての型は AnyStruct 型か AnyResource 型に分類されます。Resource型以外の全ての型をAnyStruct 型は表現できます。
var someStruct: AnyStruct = 1 // Resource型以外の全ての型を表現できる
var someResource: @AnyResource <- create TestResource()
Resourceの型の前には必ず@指示子をつけます。移動には <- 演算子を使います。
Never型
let x: Never = nil
panic関数の戻り値の型です。
Character / String型
let char: Character = "\u{FC}"
let someString: String = "Hello, world!"
各型の関数
数値型・Address型 の関数
・数値を文字列に変換する
let int8Number: Int8 = 127
let int8str String = int8Number.toString()
・big-endian の順でバイト配列を返す
let largeNumber: Int32 = 1234567890
let arr: [UInt8] = largeNumber.toBigEndianBytes() //[73, 150, 2, 210]
・文字列を数値に変換する
let int8str: String = "42"
let int8number: Int64? = Int64.fromString(int8str)
・数値の最大値を返す
let max = UInt8.max // 255
・数値の最小値を返す
let min = UInt8.min // 0
・FixedPointNumberを文字列に変換する
let fix64number: Fix64 = 1.23
let fix64str String = fix64number.toString()
・Addressを文字列に変換する
let shortAddress: Address = 0x1
let str: String = shortAddress.toString() // "0x0000000000000001"
・AddressをUInt8配列に変換する
let someAddress: Address = 0x436164656E636521
someAddress.toBytes() // is [67, 97, 100, 101, 110, 99, 101, 33]
String型の関数
・文字列の長さを取得する
let example: String = "hello"
let length: Int = example.length // 5
・文字列をUTF8エンコードによるバイト配列に変換する
let str: String = "hello"
let utf8: [UInt8] = str.utf8()
・文字列を結合する
let str1 = "hello"
let str2 = "world"
let helloWorld = str1.concat(str2) // "helloworld"
・文字列の一部を取得する
let example = "helloworld"
let slice = example.slice(from: 3, upTo: 6) // "low"
・文字列を16進文字列で表されるバイト配列に変換する
let example = "436164656e636521"
example.decodeHex() // is [67, 97, 100, 101, 110, 99, 101, 33]
・文字列を小文字に変換する
let example = "Flowers"
example.toLower() // `flowers`
・バイト配列を16進文字列に変換する
let data = [1 as UInt8, 2, 3, 0xCA, 0xDE]
String.encodeHex(data) // is "010203cade"
・文字列から1文字取得する
let str = "abc"
let c: Character = str[0] // is the Character "a"
Character型の関数
・Characterを文字列に変換する
let c: Character = "x"
c.toString() // is "x"
・Characterの配列を文字列に変換する
let rawUwU: [Character] = ["U", "w", "U"]
let uwu: String = String.fromCharacters(rawUwU) // "UwU"
Collection
Array型
宣言方法
Contract型の中、Struct型の中、Resource型の中で宣言できます。また、トランザクションコードの中でも宣言できます。スマートコントラクトの中で記載する場合は init による初期化が必要です。
access(all) contract SampleContract {
access(contract) var allNames: [String]
init() {
self.allNames = []
}
}
Contractも型ですが、この場合デプロイが行われているアドレスも含めて型とみなされます。そのためStructとResourceの型は他のContractの内部の型と混同されることはありません。
Array型の関数
・ 配列の長さを取得する
self.allNames.length
・配列を結合する
self.allNames.concat(allNames2)
・配列が値を含むかを判定する
self.allNames.contains("Kitty")
・配列に値を追加する
self.allNames.append("Jane") // This function mutates the array.
・配列に複数の値を追加する
self.allNames.appendAll("Jane", "Daisy") // This function mutates the array.
・配列に値を含む場所のインデックスを取得する
self.allNames.firstIndex(of: "Daisy")
・配列の一部を取得する
self.allNames.slice(from: 1, upTo: 3)
・配列の任意の場所に値を挿入する
self.allNames.insert(at: 2, "Daisy") // This function mutates the array.
・配列から値を取り除く
self.allNames.remove(at: 1) // This function mutates the array.
・配列から最初の値を取り除く
self.allNames.removeFirst() // This function mutates the array.
・配列から最後の値を取り除く
self.allNames.removeLast() // This function mutates the array.
Dictionary型
Dictionaryの宣言方法
Contract型の中、Struct型の中、Resource型の中で宣言できます。また、トランザクションコードの中でも宣言できます。スマートコントラクトの中で記載する場合は init による初期化が必要です。
access(all) var luckeyNumbers: {String: Int}
init() {
self.luckeyNumbers = {}
}
Struct型の中でselfを参照するとStructの型を、Resource型の中でselfを参照するとResourceの型を、Contractの中でselfを参照するとContractの型をそれぞれselfは表します。
Dictionaryの関数
・値を挿入する
self.luckeyNumbers["Daisy"] = 7
・luckeyNumbers に "Daisy" が存在しない場合にエラーを発生させたい場合は、`pre`ブロックを使用することで実現できます
pre {
self.luckeyNumbers.contains("Daisy"): "Not in luckeyNumbers"
}
・Dictionaryのすべてのキーを取得する
self.luckeyNumbers.keys
・Dictionaryのすべての値を取得する
self.luckeyNumbers.values // このメソッドはリソースを含むDictionaryでは使用できない(リソースは移動なしで値が動くことはできない)
・Dictionaryの任意の場所に値を挿入する
self.luckeyNumbers.insert(key: "Jessica", 42) // 既に値が入っている場合はOptional型でその値を返します。そうでない場合はnilを返します。
・Dictionaryから値を取り出す
self.luckeyNumbers.remove(key: "Joe") // 値が入っている場合はOptional型でその値を返します。そうでない場合はnilを返します。
・Dictionaryが値を含むかを判定する
self.luckeyNumbers.containsKey("Kitty")
・Dictionaryのすべてのキーを繰り返し処理する
self.luckeyNumbers.forEachKey(fun (key: String): Bool {
// The returned boolean value signals whether to continue
// true = `continue`
// false = `break`
return true
})
preブロックとpostブロック
pre{}ブロックはtransactionが実行される前に確認したい検証ロジック、
post{}ブロックはブロックチェーンに含まれる直前に取引の有効性を確認したい検証ロジックです。
preブロックはその名の通り、メソッドの最初で値の有効性を確認するときに使用します。値が有効でない場合パニック(トランザクション失敗)となり、 : 以降の文字列がトランザクション結果出力時に出力されます。
postブロックはその名の通り、メソッドの最後で値の有効性を確認するときに使用します。値が有効でない場合パニック(トランザクション失敗)となり、 : 以降の文字列がトランザクション結果出力時に出力されます。
pre, postはトランザクションコードでも使用でき、トランザクションの処理はprepareブロック -> preブロック -> executeブロック -> postブロックの順番に進行します。(基本的にeventの宣言以外、スマートコントラクトに書けるものはトランザクションコードでも書けます。)
event
スマートコントラクトの中に宣言し、トランザクション実行時に呼び出します。スマートコントラクトからでもトランザクションコードからでも呼び出せます。呼び出し時、Event宣言時に指定した型の引数を渡すことができ、それをGo言語やJavascriptで監視することで何が起きているかを知ることができます。Event取得処理はトランザクションIDを元にブロックチェーン情報を取得する処理より少し難易度が高いです。
