このセクションはダイナミックフィールドの内容を拡張しています。基本的なダイナミックフィールドを理解するために、まずそちらを読んでください。

ダイナミックフィールドの別のバリエーションとして、通常のダイナミックフィールドとは若干異なるダイナミックオブジェクトフィールドがあります。このセクションでは、ダイナミックオブジェクトフィールドの特徴と、通常のダイナミックフィールドとの違いについて説明します。

一般的な推奨事項として、特にIDを通じた直接的な発見が必要ない場合は、ダイナミックオブジェクトフィールドの使用を避け、(単なる)ダイナミックフィールドを優先することです。ダイナミックオブジェクトフィールドの追加コストは、それがもたらす利点によって正当化されない可能性があります。

定義

ダイナミックオブジェクトフィールドは、Sui フレームワークsui::dynamic_object_fieldsモジュールで定義されています。多くの点でダイナミックフィールドと似ていますが、ダイナミックオブジェクトフィールドはValue型に追加の制約があります。Valueはダイナミックフィールドの場合の単なるstoreではなく、keystoreの組み合わせを持つ必要があります。

これらはフレームワークの定義ではあまり明示的ではありません。概念自体がより抽象的だからです:

ファイル: 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型に同じ制約を持っています。参考のために列挙しましょう:

さらに、idメソッドがあり、これはValueオブジェクトの型を指定せずにIDを返します。

使用法とダイナミックフィールドとの違い

ダイナミックフィールドとダイナミックオブジェクトフィールドの主な違いは、後者が値としてオブジェクトのみを格納できることです。つまり、u64boolのようなプリミティブ型を格納することはできません。ダイナミックオブジェクトフィールドが別のオブジェクトにラップされていないという事実がなければ、これは制限と見なされるかもしれません。

ラッピング要件の緩和により、オブジェクトは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つのオブジェクトをロードする)が高くなります。