Abilities(機能)
Abilitiesは、Moveの型指定機能であり、特定の型の値に対して許可されるアクションを制御します。このシステムにより値の"linear"typing動作、及びグローバルストレージでの値の使用の有無と使用方法をきめ細かく制御出来ます。これは、特定のバイトコード指示へのアクセスをゲートすることで実装されます。そのため値がバイトコード指示で使用されるため必要とするAbilityが必要(必要な場合、全ての指示がAbilityによってゲートされるわけではありません)。
4つのAbility
4つのAbilityは:
copy
- このAbilityを持つ型の値をコピー出来る様にします。
drop
- このAbilityを持つ型の値をポップ/ドロップ出来る様にします。
store
- このAbilityを持つ型の値がグローバルストレージ内の構造体内に存在出来る様にします。
key
- 型をグローバルストレージ操作のキーとして提供出来る様にします。
コピー
このcopy
機能により、その機能を持つ型の値をコピー出来ます。これは、copy
演算子を使用してローカル変 数外の値をコピーする機能をゲートし、逆参照*e
を使用して参照経由で値をコピーします。
値がcopy
を持つ場合、その値内に含まれる全ての値はcopy
となります。
ドロップ
このdrop
機能により、その機能を持つ型の値をドロップできます。ドロップとは、値が転送されずMoveプログラムの実行時に実質的に破棄される事を意味します。そのためこの機能は、以下を含む多くの場所で値を無視する機能を制限します。
- ローカル変数またはパラメータの値を使用しない
;
を介したシーケンス内の値を使用しない。- 代入時の変数の値を上書きする。
*e1 = e2
の書き込み時、参照を介して値を上書きします。
値がdrop
した場合、その値内に含まれる全ての値はdrop
します。
ストア
store
機能により、この機能を持つ型の値がグローバルストレージ内の構造体(リソース)内へ存在出来る様になりますが、グローバルストレージ内の最上位のリソースとして存在出来るとは限りません。これ は操作を直接ゲートしない唯一の機能です。そのかわりkey
と併用で使用された場合、グローバルストレージの存在をゲートします。
値がstore
である場合, その値が含む全ての値はstore
です。
キー
key
機能により、型をグローバルストレージ操作のキーとして提供します。これは全てのグローバルストレージ操作をゲートし、型をmove_to
、borrow_global
、move_from
等で使用するには、型はkey
機能が必要です。
注意: 操作はkey
型が定義されているモジュールで使用する必要が有ります。(ある意味で、操作は定義モジュールに対してプライベートです)。
値がkey
である場合、その値が含む全ての値はstore
となります。これは、この種の非対称性を持つ唯一の機能です。
組み込み型
殆どのプリミティブな組み込み型にはcopy
、drop
、store
が有りますが、signer
には無くただdrop
が有ります。
bool
、u8
、u16
、u32
、u64
、u128
、u256
、address
の全てがcopy
、drop
、store
を持ちます。signer
はdrop
を持ちます。- コピー出来ず、グローバルストレージへ保存出来ません。
T
の機能に応じてvector<T>
はcopy
、drop
、store
を持つ場合が有ります。- 詳細はConditional Abilities and Generic Typesを御覧下さい。
- 不変参照の
&
と可変参照の&mut
はどちらもcopy
とdrop
を持ちます。- これは、参照先ではなく、参照内容自体をコピーして削除する事を指します。
- 参照内容はグローバルストレージへ出現出来ないため、
store
は有りません。
プリミティブ型にはkey
が無いためグローバルストレージ操作で直接使用する事は出来ません。
構造体の注釈
struct
が機能を持つ事を宣言するには、構造体名の後でありフィールドの前へhas <ability>
を宣言します。例:
struct Ignorable has drop { f: u64 }
struct Pair has copy, drop, store { x: u64, y: u64 }
この場合: Ignorable
はdrop
機能が有ります。 Pair
はcopy
、drop
、store
機能が有ります。
これらの機能は全て、ゲートされた操作に対して強力な保証を持っています。値が他のコレクション内に深くネストされている場合でも、その機能がある場合のみ値の上で操作を実行できます。
つまり、構造体の能力を宣言する場合、フィールドに特定の要件が課されます。すべてのフィールドがこれらの制約を満たす必要があります。これらのルールは、構造体が上記の機能の到達可能性ルールを満たすため必要です。構造体がその機能付きで宣言されている場合は...
copy
, 全てのフィールドはcopy
が必要です。drop
, 全てのフィールドはdrop
が必要です。store
, 全てのフィールドはstore
が必要です。key
, 全てのフィールドはstore
が必要です。key
それ自体が必要としない 現時点で唯一の機能です。
例えば:
// 何の機能も持たない構造体
struct NoAbilities {}
struct WantsCopy has copy {
f: NoAbilities, // エラー 'NoAbilities'には'copy'が有りません。
}
同様に:
// 何の機能も持たない構造体
struct NoAbilities {}
struct MyResource has key {
f: NoAbilities, // Error 'NoAbilities'には'store'が有りません。
}
条件付き機能とジェネリック型
ジェネリック型に機能が注釈付けされている場合、その型の全てのインスタンスがその機能を持つ事が保証されるわけではありません。この構造体宣言を検討して下さい。
struct Cup<T> has copy, drop, store, key { item: T }
機能に関係なくCup
が任意の型を保持出来ると非常に便利です。型システムは型パラメータを 見る 事が出来るため、その能力の保証に違反する型パラメータを見つけた場合は、Cup
から機能を削除できるはずです。
この動作は最初は少し分かりにくいかもしれませんが、コレクション型について考えると理解し易いかもしれません。組み込み型のvector
は以下の型宣言が有ると考える事が出来ます。
vector<T> has copy, drop, store;
vector
はどの型でも動作する様にしたいです。異なる機能ごとにvector
型を分けたくありません。では、必要なルールは何でしょうか? 上記のフィールドルールで必要なルールと全く同じです。つまり、内部要素をコピー出来る場合のみvector
値をコピーしても安全です。内部要素を無視/削除できる場合のみvector
値を無視しても安全です。
また、内部要素をグローバルストレージへ配置出来る場合のみvector
をグローバルストレージへ配置しても安全です。
この追加の表現力を持たせるた め、型はその型のインスタンス化に応じて宣言された全ての機能を持つとは限りません。そのかわり、型が持つ機能はその宣言 と 型引数次第です。どの型でも悲観的に、型パラメータは構造体内で使用されると想定されるため、型パラメータがフィールドに対して上記で説明した要件を満たしている場合のみ機能が付与されます。上記の例からCup
を取り上げます。
Cup
はT
がcopy
を持っている場合のみcopy
機能を持ちます。T
がdrop
を持つ場合のみdrop
を持ちます。T
がstore
を持つ場合のみstore
を持ちます。T
がstore
を持つ場合のみkey
を持ちます。
各機能の条件付きシステムの例がこちら。
例: 条件付きcopy
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
fun example(c_x: Cup<u64>, c_s: Cup<S>) {
// 有効, 'u64'が'copy'を持っているので'Cup<u64>'は'copy'を持っています。
let c_x2 = copy c_x;
// 有効, 'S'が'copy'を持っているので'Cup<S>'は'copy'を持っています。
let c_s2 = copy c_s;
}
fun invalid(c_account: Cup<signer>, c_n: Cup<NoAbilities>) {
// 無効, 'Cup<signer>'は'copy'を持っていません。
// 'Cup'がcopyで宣言されているにも関わらず、インスタンスには'copy'が有りません。
// 'signer'には'copy'が無いため
let c_account2 = copy c_account;
// 無効, 'Cup<NoAbilities>'には'copy'が有りません。
// 'NoAbilities'には'copy'が無いため
let c_n2 = copy c_n;
}
例: 条件付きdrop
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
fun unused() {
Cup<bool> { item: true }; // 有効, 'Cup<bool>'には'drop'が有ります。
Cup<S> { item: S { f: false }}; // 有効, 'Cup<S>'には'drop'が有ります。
}
fun left_in_local(c_account: Cup<signer>): u64 {
let c_b = Cup<bool> { item: true };
let c_s = Cup<S> { item: S { f: false }};
// 有効なリターン: 'c_account'、'c_b'、'c_s'には値が有ります。
// ただし'Cup<signer>'、'Cup<bool>'、'Cup<S>'には'drop'が有ります。
0
}
fun invalid_unused() {
// 無効, 'drop'が無いため'Cup<NoAbilities>'は無視出来ません。
// 'Cup'が'drop'で宣言されているにも関わらずインスタンスには'drop'が有りません。
// 'NoAbilities'には'drop'が無いため。
Cup<NoAbilities> { item: NoAbilities {}};
}
fun invalid_left_in_local(): u64 {
let c_n = Cup<NoAbilities> { item: NoAbilities {}};
// 無効なリターン: 'c_n'には値が有ります。
// 'Cup<NoAbilities>'には'drop'が有りません。
0
}
例: 条件付き store
struct Cup<T> has copy, drop, store { item: T }
// 'MyInnerResource'は'store'で宣言されているため全てのフィールドは'store'が必要です。
struct MyInnerResource has store {
yes: Cup<u64>, // 有効, 'Cup<u64>'には'store'が有ります。
// no: Cup<signer>,無効, 'Cup<signer>'には'store'が有りません。
}
// 'MyResource'は'key'で宣言されているため、全てのフィールドは'store'が必要です。
struct MyResource has key {
yes: Cup<u64>, // 有効, 'Cup<u64>'には'store'が有ります。
inner: Cup<MyInnerResource>, // 有効, 'Cup<MyInnerResource>'には'store'が有ります。
// no: Cup<signer>, 無効, 'Cup<signer>'には'store'が有りません。
}
例: 条件付き key
struct NoAbilities {}
struct MyResource<T> has key { f: T }
fun valid(account: &signer) acquires MyResource {
let addr = signer::address_of(account);
// 有効, 'MyResource<u64>'には'key'が有ります。
let has_resource = exists<MyResource<u64>>(addr);
if (!has_resource) {
// 有効, 'MyResource<u64>'には'key'が有ります。
move_to(account, MyResource<u64> { f: 0 })
};
// 有効, 'MyResource<u64>'には'key'が有ります。
let r = borrow_global_mut<MyResource<u64>>(addr)
r.f = r.f + 1;
}
fun invalid(account: &signer) {
// 無効, 'MyResource<NoAbilities>'には'key'が有りません。
let has_it = exists<MyResource<NoAbilities>>(addr);
// 無効, 'MyResource<NoAbilities>'には'key'が有りません。
let NoAbilities {} = move_from<NoAbilities>(addr);
// 無効, 'MyResource<NoAbilities>'には'key'が有りません。
move_to(account, NoAbilities {});
// 無効, 'MyResource<NoAbilities>'には'key'が有りません 。
borrow_global<NoAbilities>(addr);
}