このセクションはダイナミックフィールドの内容を拡張しています。基本的なダイナミックフィールドを理解するために、まずそちらを読んでください。
ダイナミックフィールドの別のバリエーションとして、通常のダイナミックフィールドとは若干異なるダイナミックオブジェクトフィールドがあります。このセクションでは、ダイナミックオブジェクトフィールドの特徴と、通常のダイナミックフィールドとの違いについて説明します。
一般的な推奨事項として、特にIDを通じた直接的な発見が必要ない場合は、ダイナミックオブジェクトフィールドの使用を避け、(単なる)ダイナミックフィールドを優先することです。ダイナミックオブジェクトフィールドの追加コストは、それがもたらす利点によって正当化されない可能性があります。
ダイナミックオブジェクトフィールドは、Sui フレームワークのsui::dynamic_object_fields
モジュールで定義されています。多くの点でダイナミックフィールドと似ていますが、ダイナミックオブジェクトフィールドはValue
型に追加の制約があります。Value
はダイナミックフィールドの場合の単なるstore
ではなく、key
とstore
の組み合わせを持つ必要があります。
これらはフレームワークの定義ではあまり明示的ではありません。概念自体がより抽象的だからです:
ファイル: sui-framework/sources/dynamic_object_fields.move
/// フィールドと値に関連付けられた名前を格納するための内部オブジェクト。
/// 別の型が必要なのは、dynamic_fieldの直接使用によるキーの衝突を防ぐためです。
public struct Wrapper<Name> has copy, drop, store {
name: Name,
}
ダイナミックフィールドセクションのField
型とは異なり、Wrapper
型はフィールドの名前のみを格納します。値はオブジェクト自体であり、ラップされていません。
Value
型の制約は、ダイナミックオブジェクトフィールドで利用可能なメソッドに現れます。以下はadd
関数のシグネチャです:
/// `object: &mut UID`のオブジェクトに、`name: Name`で指定されたフィールドに
/// ダイナミックオブジェクトフィールドを追加します。オブジェクトがすでにその名前のフィールドを
/// 持っている場合、`EFieldAlreadyExists`でアボートします。
public fun add<Name: copy + drop + store, Value: key + store>(
// アクセス制御のために&mut UIDを複数箇所で使用します
object: &mut UID,
name: Name,
value: Value,
) { /* 実装は省略 */ }
ダイナミックフィールドセクションと同一の他のメソッドも、Value
型に同じ制約を持っています。参考のために列挙しましょう:
add
- オブジェクトにダイナミックオブジェクトフィールドを追加します。remove
- オブジェクトからダイナミックオブジェクトフィールドを削除します。borrow
- オブジェクトからダイナミックオブジェクトフィールドを借用します。borrow_mut
- オブジェクトからダイナミックオブジェクトフィールドへの可変参照を借用します。exists_
- ダイナミックオブジェクトフィールドが存在するかチェックします。exists_with_type
- 特定の型のダイナミックオブジェクトフィールドが存在するかチェックします。さらに、id
メソッドがあり、これはValue
オブジェクトの型を指定せずにID
を返します。
ダイナミックフィールドとダイナミックオブジェクトフィールドの主な違いは、後者が値としてオブジェクトのみを格納できることです。つまり、u64
やbool
のようなプリミティブ型を格納することはできません。ダイナミックオブジェクトフィールドが別のオブジェクトにラップされていないという事実がなければ、これは制限と見なされるかもしれません。
ラッピング要件の緩和により、オブジェクトはIDを通じてオフチェーンで発見可能な状態を保ちます。しかし、ラップされたオブジェクトのインデックス作成が実装された場合、この特性は際立ったものではなくなり、ダイナミックオブジェクトフィールドは冗長な機能となる可能性があります。
module book::dynamic_object_field {
use std::string::String;
// 長いモジュール名には2つの一般的な別名があります。:`dof`と`ofield`
// 両方が一般的に使用され、さまざまなプロジェクトで見られます。
use sui::dynamic_object_field as dof;
use sui::dynamic_field as df;
/// 例で使用する`Character`
public struct Character has key { id: UID }
/// `key`能力を持たないメタデータ
public struct Metadata has store, drop { name: String }
/// `key`と`store`能力を持つアクセサリー
public struct Accessory has key, store { id: UID }
#[test]
fun equip_accessory() {
let ctx = &mut tx_context::dummy();
let mut character = Character { id: object::new(ctx) };
// アクセサリーを作成し、キャラクターに装着する
let hat = Accessory { id: object::new(ctx) };
// 帽子をキャラクターに追加する。`dynamic_fields`と同様に
dof::add(&mut character.id, b"hat_key", hat);
// ただし、keyでない構造体には`dynamic_field`のみ使用可能
df::add(&mut character.id, b"metadata_key", Metadata {
name: b"John".to_string()
});
// キャラクターから帽子を借用する
let hat_id = dof::id(&character.id, b"hat_key").extract(); // Option<ID>
let hat_ref: &Accessory = dof::borrow(&character.id, b"hat_key");
let hat_mut: &mut Accessory = dof::borrow_mut(&mut character.id, b"hat_key");
let hat: Accessory = dof::remove(&mut character.id, b"hat_key");
// クリーンアップ、Metadataは孤立しています。
sui::test_utils::destroy(hat);
sui::test_utils::destroy(character);
}
}
ダイナミックオブジェクトフィールドは、ダイナミックフィールドよりも少し高価です。内部構造のため、NameのWrapperとValueの2つのオブジェクトが必要です。このため、オブジェクトフィールドの追加とアクセスのコスト(ダイナミックフィールドの1つに対して2つのオブジェクトをロードする)が高くなります。