サポートされている型
この章では、Candid でサポートされているすべての型をリストアップしています。 それぞれの型について、以下の情報が記載されています:
-
型の構文と、型のテキスト表現の構文。
-
それぞれの型に対する Candid のアップグレードに関するルールは、型の サブタイプ と スーパータイプ によって決まります。
-
それぞれの型に対応する Rust、Motoko、Javascript の型。
既存メソッドの 返り値 の型は以前の型のサブタイプに変更できます。 既存メソッドの 引数 の型は以前の型のスーパータイプに変更できます。
このリファレンスには、それぞれの型に関連する特定のサブタイプとスーパータイプのみが記載されていることに注意してください。
どの型にも適用可能なサブタイプやスーパータイプは記載していません。
たとえば、empty
は他のどの型のサブタイプにもなりうるので、このリファレンスではサブタイプとして挙げていません。
同様に、reserved
と opt t
型 は、任意の型のスーパータイプであるため、特定の型のスーパータイプとして挙げていません。
empty
、reserved
、opt t
型のサブタイプ化のルールに関する詳細については、以下の節を参照してください。
text 型
text
型は、人が読めるテキストが必要な場面で使われます。より正確には、unicode の符号位置のシーケンスです(サロゲート部分を除く)。
- 型の構文
-
text
- テキスト表現の構文
-
"" "Hello" "Escaped characters: \n \r \t \\ \" \'" "Unicode escapes: \u{2603} is ☃ and \u{221E} is ∞" "Raw bytes (must be utf8): \E2\98\83 is also ☃"
- 対応する Motoko の型
-
Text
- 対応する Rust の型
-
String
または&str
- 対応する JavaScript の値
-
"String"
blob 型
blob
型は、バイナリデータ(バイトのシーケンス)に用いることができます。
blob
型を用いて書かれたインターフェースは、vec nat8
を用いて書かれたインターフェースと互換性があります。
- 型の構文
-
blob
- テキスト表現の構文
-
blob <text>
ここで、
<text>
は、すべての文字が UTF8 エンコーディングで表現された文字列リテラルと任意のバイトシーケンス("\CA\FF\FE"
)を表します。Text 型に関して詳しく知りたい場合は、Text 型を参照してください。
- サブタイプ
-
vec nat8
およびvec nat8
の全てのサブタイプ。 - スーパータイプ
-
vec nat8
およびvec nat8
の全てのスーパータイプ。 - 対応する Motoko の型
-
Blob
- 対応する Rust の型
-
Vec<u8>
または&[u8]
- 対応する JavaScript の値
-
[ 1, 2, 3, 4, ... ]
nat 型
nat
型は、すべての(非負の)自然数を含みます。
値の大きさに制限はなく、任意の大きな数字を表すことができます。
通信時のエンコーディングは LEB128 なので、小さな数字も効率よく表現できます。
- 型の構文
-
nat
- テキスト表現の構文
-
1234 1_000_000 0xDEAD_BEEF
- スーパータイプ
-
int
- 対応する Motoko の型
-
Nat
- 対応する Rust の型
-
candid::Nat
またはu128
- 対応する JavaScript の値
-
BigInt(10000)
または10000n
int 型
int
型はすべての整数を含みます。
大きさに制限がなく、任意の大小の数値を表現することができます。
通信時のエンコーディングは SLEB128 なので、小さな数字も効率的に表現できます。
- 型の構文
-
int
- テキスト表現の構文
-
1234 -1234 +1234 1_000_000 -1_000_000 +1_000_000 0xDEAD_BEEF -0xDEAD_BEEF +0xDEAD_BEEF
- サブタイプ
-
nat
- 対応する Motoko の型
-
Int
- 対応する Rust の型
-
candid::Int
またはi128
- 対応する JavaScript の値
-
BigInt(-10000)
または-10000n
natN 型と intN 型
nat8
、nat16
、nat32
、nat64
、int8
、int16
、int32
、int64
の型は、そのビット数の表現を持つ数値を表し、より低レベルなインターフェースで使用することができます。
natN
の範囲は {0 …. 2^N-1}
であり、intN
の範囲は -2^(N-1) … 2^(N-1)-1
となります。
通信時の表現は、ちょうどその長さのビット数になります。そのため、小さな値に対しては、nat64
よりも nat
の方が容量の効率が良いです。
- 型の構文
-
nat8
,nat16
,nat32
,nat64
,int8
,int16
,int32
またはint64
- テキスト表現の構文
-
nat8
,nat16
,nat32
,nat64
はnat
と同じです。int8
,int16
,int32
,int64
はint
と同じです。型アノテーションを使って、異なる整数型を区別することができます。
100 : nat8 -100 : int8 (42 : nat64)
- 対応する Motoko の型
-
natN
はデフォルトではNatN
に翻訳されますが、必要に応じてWordN
にも翻訳されます。intN
はIntN
に翻訳されます。 - 対応する Rust の型
-
同サイズの符号付き整数と符号なし整数に対応します。
ビット長 符号付き 符号なし 8-bit
i8
u8
16-bit
i16
u16
32-bit
i32
u32
64-bit
i64
u64
- 対応する JavaScript の値
-
8-bit, 16-bit, 32-bit は number 型に翻訳されます。
int64
とnat64
は JavaScript のBigInt
プリミティブに翻訳されます。
float32 型と float64 型
float32
型および float64
型は,IEEE 754 の浮動小数点数を、単精度(32ビット)および倍精度(64ビット)で表したものです。
- 型の構文
-
float32
,float64
- テキスト表現の構文
-
int
と同じ構文で、次のように浮動小数点リテラルが加わります:1245.678 +1245.678 -1_000_000.000_001 34e10 34E+10 34e-10 0xDEAD.BEEF 0xDEAD.BEEFP-10 0xDEAD.BEEFp+10
- 対応する Motoko の型
-
float64
はFloat
に対応します。float32
は、現在、Motoko での表現はありません。float32
を使った Candid インターフェースは、Motoko のプログラムからは生成できませんし、利用することもできません。 - 対応する Rust の型
-
f32
,f64
- 対応する JavaScript の値
-
float number
bool 型
bool
型は論理値を示すデータ型で、true
または false
の値のみを持つことができます。
- 型の構文
-
bool
- テキスト表現の構文
-
true
,false
- 対応する Motoko の型
-
Bool
- 対応する Rust の型
-
bool
- 対応する JavaScript の値
-
true
,false
null 型
null
型は値 null
の型であり、全ての opt t
型のサブタイプです。また、バリアントを使用して列挙型をモデル化する際に慣例的に使用されます。
- 型の構文
-
null
- テキスト表現の構文
-
null
- スーパータイプ
-
全ての
opt t
型。 - 対応する Motoko の型
-
Null
- 対応する Rust の型
-
()
- 対応する JavaScript の値
-
null
vec t 型
vec
型はベクター(シーケンス、リスト、配列)を表します。
vec t
型の値は、t
型の 0 個以上の値のシーケンスを含みます。
- 型の構文
-
vec bool
,vec nat8
,vec vec text
など。 - テキスト表現の構文
-
vec {} vec { "john@doe.com"; "john.doe@example.com" };
- サブタイプ
-
-
t
がt'
のサブタイプであるときはいつでも、vec t
はvec t'
のサブタイプです。 -
blob
はvec nat8
のサブタイプです。
-
- スーパータイプ
-
-
t
がt'
のスーパータイプであるときはいつでも、vec t
はvec t'
のスーパータイプです。 -
blob
はvec nat8
のスーパータイプです。
-
- 対応する Motoko の型
-
[T]
となります。ここで、Motoko 型のT
はt
に対応しています。 - 対応する Rust の型
-
Vec<T>
または&[T]
となります。ここで、Rust 型のT
はt
に対応しています。vec t
はBTreeSet
またはHashSet
に翻訳されます。vec record { KeyType; ValueType }
は、BTreeMap
またはHashMap
に翻訳されます。 - 対応する JavaScript の値
-
Array
例えば[ "text", "text2", … ]
opt t 型
opt t
型は、t
型のすべての値と、特殊な値である null
を含みます。
これは、ある値が任意であることを表現するのに使われます。つまり、データは t
型の値として存在するかもしれないし、null
という値として存在しないかもしれない、ということです。
opt
型は入れ子にすることができ(例:opt opt text
)、値 null
と opt null
は別の値です。
opt
型は、Candid インターフェース のアップグレードにおいて重要な役割を果たしており、以下のような特別なサブタイプのルールを持っています。
- 型の構文
-
opt bool
,opt nat8
,opt opt text
など。 - テキスト表現の構文
-
null opt true opt 8 opt null opt opt "test"
- サブタイプ
-
opt
を使ったサブタイプの規範的なルールは次の通りです:-
t
がt'
のサブタイプであるときはいつでも、opt t
はopt t'
のサブタイプです。 -
null
はopt t'
のサブタイプです。 -
t
はopt t
のサブタイプです(t
自体がnull
でない限り、opt …
またはreserved
)。
加えて、アップグレードや上位のサービスに関する技術的な理由から、 every 型は
opt t
のサブタイプであり、型が一致しない場合にはnull
が生成されます。ただし、ユーザーはこのルールを直接利用しないようにしてください。 -
- スーパータイプ
-
-
t
がt'
のスーパータイプであるとき、opt t
はopt t'
のスーパータイプです。
-
- 対応する Motoko の型
-
?T
となります。ここで、Motoko 型のT
がt
に対応しています。 - 対応する Rust の型
-
Option<T>
となります。ここで、Rust 型のT
がt
に対応しています。 - 対応する JavaScript の値
-
null
は[]
に翻訳されます。opt 8
は[8]
に翻訳されます。opt opt "test"
は[["test"]]
に翻訳されます。
record { n : t, … } 型
record
型はラベル付けされた値の集まりです。例えば、以下のコードはテキストフィールドの street
、city
、country
と数値フィールドの zip_code
を持つ record の型に address
という名前を与えています。
type address = record {
street : text;
city : text;
zip_code : nat;
country : text;
};
record 型宣言のフィールドの順序は重要ではありません。 各フィールドは異なる型を持つことができます(同じ型のみを持つことができる vector とは異なります)。 record フィールドのラベルは、以下の例のように 32 ビットの自然数にすることもできます。
type address2 = record {
288167939 : text;
1103114667 : text;
220614283 : nat;
492419670 : text;
};
実際のところテキストラベルはその ハッシュ値 として扱われますし、さらに言えば address
と address2
は Candid にとって同じ型です。
ラベルを省略すると、Candid は自動的に順次昇順のラベルを割り当てます。この挙動により,以下のような短縮された構文になり、通常ペアやタプルを表現するのに使われます。record { text; text; opt bool }
は、record { 0 : text; 1: text; 2: opt bool }
と同等です。
- 型の構文
-
record {} record { first_name : text; second_name : text } record { "name with spaces" : nat; "unicode, too: ☃" : bool } record { text; text; opt bool }
- テキスト表現の構文
-
record {} record { first_name = "John"; second_name = "Doe" } record { "name with spaces" = 42; "unicode, too: ☃" = true } record { "a"; "tuple"; null }
- サブタイプ
-
record のサブタイプとは、(任意のタイプの)フィールドが追加されたり、フィールドの型がサブタイプに変更されたり、選択型のフィールドが削除されたりした record 型のことです。ただし、メソッドの返り値で選択型のフィールドを削除するのはバッドプラクティスです。フィールドの型を
opt empty
に変更することで、そのフィールドがもう使われていないことを示すことができます。例えば、次のような record を返す関数があったとします:
record { first_name : text; middle_name : opt text; second_name : text; score : int }
上の record は、次のような record に更新することができます:
record { first_name : text; middle_name : opt empty; second_name : text; score : nat; country : text }
ここでは、
middle_name
フィールドを非推奨とし、score
の型を変更し、country
フィールドを追加しています。 - スーパータイプ
-
record のスーパータイプとは、一部のフィールドが削除された record 型、一部のフィールドのタイプがスーパータイプに変更された record 型、または選択型のフィールドが追加された record 型のことです。
後者は、引数の record を追加フィールドで拡張することができるものです。古いインターフェースを使用しているクライアントは、 record にフィールドを含めることができず、アップグレードされたサービスで期待される
null
としてデコードされます。例えば、レコード 型を期待する関数があるとします。
record { first_name : text; second_name : text; score : nat }
以下の record を受け取る関数に更新することができます。
record { first_name : text; score: int; country : opt text }
- 対応する Motoko の型
-
record 型がタプル(例えば、0 から始まる連続したラベル)を参照している場合は、Motoko のタプル型(例えば
(T1, T2, T3)
)が使用されます。それ以外の場合は、Motoko の record({ first_name :Text, second_name : Text })
が使用されます。フィールド名が Motoko の予約語の場合は、アンダースコア が付加されます。つまり、
record { if : bool }
は、{ if_ : Bool }
となります。フィールド名が Motoko の有効な識別子でない場合は、代わりに フィールド のハッシュが使われます。例えば、
record { ☃ : bool }
は{ 11272781 : Boolean }
となります。 - 対応する Rust の型
-
derive(CandidType, Deserialize)]
というトレイトを持つ、ユーザ定義の構造体
となります。フィールド名を変更するには、
#[serde(rename = "DifferentFieldName")]
属性を使用します。record 型がタプルの場合は、
(T1, T2, T3)
のようなタプル型に変換されます。 - 対応する JavaScript の値
-
record 型がタプルの場合、配列に変換されます。例えば、
["Candid", 42]
のようになります。それ以外の場合は、record オブジェクトに翻訳されます。例えば、
{ "first name": "Candid", age: 42 }
のようになります.フィールド名がハッシュの場合は、フィールド名として
_hash_
を使用します。例えば、{ _1_: 42, "1": "test" }
のようになります。
variant { n : t, … } 型
variant
型は、定義された値の組み合わせ(あるいは タグ)のうちの 1 つの値を表します。つまり、以下の variant 型は、dot、circle(半径が与えられる)、rectangle(寸法が与えられる)、吹き出し(テキストが与えられる)のいずれかです。なお、吹き出しは、ユニコードのラベル(💬)の使用が可能であることを例示しています。
type shape = variant {
dot : null;
circle : float64;
rectangle : record { width : float64; height : float64 };
"💬" : text;
};
variant
型のタグは、record 型のラベルと同様、実際には数字であり、文字列のタグはそのハッシュ値を指します。
しばしば、タグの一部(または全部)がデータを持たないことがあります。このような場合、上記の dot
のように、null
型を使用するのが慣例です。実際、Candid はこのような使い方を推奨しており、variant では : null
型のアノテーションを省略することができます。つまり、
type season = variant { spring; summer; fall; winter }
は以下と等価であり、
type season = variant {
spring : null; summer: null; fall: null; winter : null
}
となります。これは列挙を表現するのに使われます。
variant {}
型は構文上問題ありませんが、値を持っていません。値がないことを意図するのであれば、empty
型の方が適切かもしれません。
- 型の構文
-
variant {} variant { ok : nat; error : text } variant { "name with spaces" : nat; "unicode, too: ☃" : bool } variant { spring; summer; fall; winter }
- テキスト表現の構文
-
variant { ok = 42 } variant { "unicode, too: ☃" = true } variant { fall }
- サブタイプ
-
variant 型のサブタイプは、一部のタグを削除し、一部のタグの型をサブタイプに変更した variant 型です。
メソッドの返り値の variant に新しいタグを 追加 できるようにしたい場合、variant 自体が
opt …
でラップされていれば可能です。これには事前の計画が必要です。インターフェースを設計する際には、次のように書く代わりに:service { get_member_status (member_id : nat) -> (variant {active; expired}); }
以下のように書くのが良いでしょう:
service { get_member_status (member_id : nat) -> (opt variant {active; expired}); }
このようにすることで、後に
名誉
会員ステータスを追加する必要が生じた場合に、ステータスのリストを拡張することができます。古いクライアントは未知のフィールドをnull
として受け取ります。 - スーパータイプ
-
variant 型のスーパータイプは、タグが追加された variant です。一部のタグの型がスーパータイプに変更されている場合もあります。
- 対応する Motoko の型
-
variant 型は、以下のように Motoko の variant 型として表現されます:
type Shape = { #dot : (); #circle : Float; #rectangle : { width : Float; height : Float }; #_2669435721_ : Text; };
列挙型を variant としてモデル化する際、Candid と Motoko それぞれの慣例の対応付けを行う必要があるため、タグの型が
null
の場合は Motoko では()
に対応することに注意してください。 - 対応する Rust の型
-
#[derive(CandidType, Deserialize)]
トレイトを持つユーザー定義のenum
となります。フィールド名を変更するには、
#[serde(rename = "DifferentFieldName")]
属性を使用することができます。 - 対応する JavaScript の値
-
1 つの要素を持つ record オブジェクトとなります。例えば、
{ dot: null }
のようになります。フィールド名がハッシュ値の場合には、フィールド名として
_hash_
を用います。例えば、{ _2669435721_: "test" }
のようになります。
func (…) → (…) 型
Candid は、上位のユースケースをサポートするように設計されており、あるサービスが他のサービスやそのメソッドへの参照を受け取ったり、提供したりすることができます(例:コールバック関数)。
func
型はこの目的において中心的な役割を果たします。これは、関数の シグネチャ (引数や返り値の型、アノテーション)を示しており、この型の値は、そのシグネチャを持つ関数への参照となります。
サポートされているアノテーションは以下の通りです:
-
query
は、Canister のステートを変更せず、安価なクエリコールのメカニズムを使用して呼び出すことができることを意味しています。 -
oneway
は、この関数が何のレスポンスも返さないことを示します。これは、Fire and Forget シナリオ(訳註:イベントハンドラなど、非同期呼び出しで関数を投げ放す場合)を想定しています。
引数の命名について詳しく知りたい方は、引数と返り値の命名を参照してください。
- 型の構文
-
func () -> () func (text) -> (text) func (dividend : nat, divisor : nat) -> (div : nat, mod : nat); func () -> (int) query func (func (int) -> ()) -> ()
- テキスト表現の構文
-
現在、プリンシパルによって識別されるサービスのパブリックメソッドのみサポートされています。
func "w7x7r-cok77-xa".hello func "w7x7r-cok77-xa"."☃" func "aaaaa-aa".create_canister
- サブタイプ
-
サービスのアップグレードのルールで説明されているように、以下の修正は、ある func 型をそのサブタイプに変更します:
-
返り値の型のリストを拡張することができます。
-
引数の型のリストを短くすることができます。
-
引数の型のリストを、オプションの引数(
opt …
型)で拡張することができます。 -
既存の引数の型を スーパータイプ に変更することができます。言い換えれば、関数の型は引数の型に 反変 であるということです。
-
既存の返り値の型をサブタイプに変更することができます。
-
- スーパータイプ
-
以下の修正は、ある func 型をそのスーパータイプに変更します:
-
返り値の型のリストを短くすることができます。
-
返り値の型のリストはオプションの引数(
opt …
型)で拡張することができます。 -
引数の型のリストは拡張さすることができます。
-
既存の引数の型を サブタイプ に変更することができます。言い換えれば、関数の型は引数の型に 反変 であるということです。
-
既存の返り値の型をスーパータイプに変更することができます。
-
- 対応する Motoko の型
-
Candid の関数型は、Motoko の
shared
関数型に対応しており、返り値の型はasync
でラップされています(oneway
でアノテーションされていない限り、返り値の型は単に()
となります)。引数と返り値はタプルになりますが、1 つだけ指定されている場合はタプルにならず、直接使用されます:type F0 = func () -> (); type F1 = func (text) -> (text); type F2 = func (text, bool) -> () oneway; type F3 = func (text) -> () oneway; type F4 = func () -> (text) query;
は、Motoko では以下に対応します:
type F0 = shared () -> async (); type F1 = shared Text -> async Text; type F2 = shared (Text, Bool) -> (); type F3 = shared (text) -> (); type F4 = shared query () -> async Text;
- 対応する Rust の型
-
candid::IDLValue::Func(Principal, String)
となります。詳しくは、 IDLValue を参照ください。 - 対応する JavaScript の値
-
[Principal.fromText("aaaaa-aa"), "create_canister"]
service {…} 型
サービスは、それぞれの関数(func
型を使用)だけでなく、サービス全体への参照を渡したい場合があります。このような場合には、Candid の型はサービスの(完全な)インターフェースを宣言するために使うことができます。
service 型の構文に関する詳細は、Candid Service の記述を参照してください。
- 型の構文
-
service { add : (nat) -> (); subtract : (nat) -> (); get : () -> (int) query; subscribe : (func (int) -> ()) -> (); }
- テキスト表現の構文
-
service "w7x7r-cok77-xa" service "zwigo-aiaaa-aaaaa-qaa3a-cai" service "aaaaa-aa"
- サブタイプ
-
service 型のサブタイプとは、追加のメソッドが付与されたり、既存のメソッドの型がサブタイプに変更されている service 型です。
これは、Service のアップグレード内のルールにて説明されているのと同じ原理に基づくものです。
- スーパータイプ
-
service 型のスーパータイプとは、一部のメソッドが削除されたり、既存のメソッドの型がスーパータイプに変更されている service 型です。
- 対応する Motoko の型
-
Candid の Service 型は Motoko の
actor
型に直接対応します:actor { add : shared Nat -> async () subtract : shared Nat -> async (); get : shared query () -> async Int; subscribe : shared (shared Int -> async ()) -> async (); }
- 対応する Rust の型
-
candid::IDLValue::Service(Principal)
に対応します。詳しくは、 IDLValue を参照してください。 - 対応する JavaScript の値
-
Principal.fromText("aaaaa-aa")
principal 型
Internet Computer では、Canister やユーザーや他のエンティティを識別するための共通の方式として、principal を使用しています。
- 型の構文
-
principal
- テキスト表現の構文
-
principal "w7x7r-cok77-xa" principal "zwigo-aiaaa-aaaaa-qaa3a-cai" principal "aaaaa-aa"
- 対応する Motoko の型
-
Principal
- 対応する Rust の型
-
candid::Principal
またはic_types::Principal
- 対応する JavaScript の値
-
Principal.fromText("aaaaa-aa")
reserved 型
reserved
型は、1つの(情報を持たない)値 reserved
を持つ型で、他のすべての型のスーパータイプです。
メソッドの引数を削除するのに reserved
型を使用することができます。次のようなシグネチャを持つメソッドを考えてみましょう:
service {
foo : (first_name : text, middle_name : text, last_name : text) -> ()
}
ここで、middle_name
をもはや使わなくなったと仮定します。ところが、Candid はあなたが関数シグネチャを以下のように変更することを妨げません:
service {
foo : (first_name : text, last_name : text) -> ()
}
これは非常に危険です。なぜなら、クライアントが古いインターフェースを使ってコールした場合、この関数は黙って last_name
を無視し、middle_name
を last_name
として受け取ることになるからです。メソッドの引数名は単なる慣例であり、メソッドの引数はその位置によって識別されることを思い出してください。
代わりに、以下のようにすることができます:
service {
foo : (first_name : text, middle_name : reserved, last_name : text) -> ()
}
これは、foo
は以前は第 2 引数を使用していたものの、現在は使用していないということを示しています。
将来引数が変わることが予想される関数や、型ではなく位置でしか区別できない引数を持つ関数は、1つの record を取るように宣言するというパターンを採用することで、この落とし穴を回避することができます。 例えば以下のようになります:
service {
foo : (record { first_name : text; middle_name : text; last_name : text}) -> ()
}
ここで、関数シグネチャを以下のように変更します:
service {
foo : (record { first_name : text; last_name : text}) -> ()
}
これは正しく動作します。このようにすることで、削除された引数に関する記録を残す必要もありません。
一般的に、メソッドから引数を削除することは推奨されません。通常は、引数を省略した新しいメソッドを導入することが望ましいです。 |
- 型の構文
-
reserved
- テキスト表現の構文
-
reserved
- サブタイプ
-
全ての型
- 対応する Motoko の型
-
Any
- 対応する Rust の型
-
candid::Reserved
- 対応する JavaScript の値
-
任意の値
empty 型
empty
型は、値を持たない型で、他のどの型のサブタイプでもあります。
empty
型の実用的なユースケースは比較的まれです。
例えば、empty
型は、あるメソッドが「決して正常にリターンしない」ことを示すために使用することができます:
service : {
always_fails () -> (empty)
}
- 型の構文
-
empty
- テキスト表現の構文
-
この型には値がないため、テキスト表現はありません。
- スーパータイプ
-
全ての型
- 対応する Motoko の型
-
None
- 対応する Rust の型
-
candid::Empty
- 対応する JavaScript の値
-
この型には値がないため、対応する JavaScript の値はありません。