Sui オブジェクトモデルでは、オブジェクトを他のオブジェクトに ダイナミックフィールド として添付することができます。この動作は、他のプログラミング言語の Map の動作に似ています。しかし、Move では厳密に型付けされる Mapコレクションのセクションで説明しました)とは異なり、ダイナミックフィールドでは任意の型のオブジェクトを添付することができます。フロントエンド開発の世界からの類似のアプローチとしては、任意の型のデータを動的に格納できる JavaScript の Object 型があります。

オブジェクトに添付できるダイナミックフィールドの数に制限はありません。したがって、ダイナミックフィールドを使用して、オブジェクトのサイズ制限に収まらない大量のデータを格納することができます。

ダイナミックフィールドは、オブジェクトサイズの制限を回避するためにデータを小さな部分に分割することから、アプリケーションロジックの一部としてオブジェクトを添付することまで、幅広い用途に使用できます。

定義

ダイナミックフィールドは、Sui フレームワークsui::dynamic_field モジュールで定義されています。これらは 名前 を介してオブジェクトの UID に添付され、その名前を使用してアクセスできます。オブジェクトに添付できる同じ名前のフィールドは1つだけです。

ファイル: sui-framework/sources/dynamic_field.move

/// フィールドと値を格納するための内部オブジェクト
public struct Field<Name: copy + drop + store, Value: store> has key {
    /// オブジェクトID、フィールド名の値、その型のハッシュによって決定される
    /// つまり、hash(parent.id || name || Name)
    id: UID,
    /// このフィールドの名前の値
    name: Name,
    /// このフィールドに紐づけられた値
    value: Value,
}

定義が示すように、ダイナミックフィールドは内部の Field オブジェクトに格納され、オブジェクト ID、フィールド名、フィールドの型に基づいて決定論的に生成された UID を持ちます。Field オブジェクトには、フィールド名とそれに紐づけられた値が含まれています。NameValue の型パラメータの制約は、キーと値が持つべき能力を定義しています。

使用方法

ダイナミックフィールドで利用可能なメソッドは簡単です:フィールドは add で追加し、remove で削除し、borrowborrow_mut で読み取ることができます。さらに、exists_ メソッドを使用してフィールドが存在するかどうかを確認できます(型でより厳密にチェックするには、exists_with_type メソッドがあります)。

module book::dynamic_collection {
    // `dynamic_field` の非常に一般的なエイリアスは `df` です
    // モジュール名がかなり長いため
    use sui::dynamic_field as df;
    use std::string::String;

    /// ダイナミックフィールドを添付するオブジェクト。
    public struct Character has key {
        id: UID
    }

    // キャラクターに添付できる様々なアクセサリーのリスト。
    // これらは `store` 能力を持つ必要があります。
    public struct Hat has key, store { id: UID, color: u32 }
    public struct Mustache has key, store { id: UID }

    #[test]
    fun test_character_and_accessories() {
        let ctx = &mut tx_context::dummy();
        let mut character = Character { id: object::new(ctx) };

        // キャラクターの UID に帽子を添付
        df::add(
            &mut character.id,
            b"hat_key",
            Hat { id: object::new(ctx), color: 0xFF0000 }
        );

        // 同様に、キャラクターの UID に口ひげを添付
        df::add(
            &mut character.id,
            b"mustache_key",
            Mustache { id: object::new(ctx) }
        );

        // 帽子と口ひげがキャラクターに添付されていることを確認
        //
        assert!(df::exists_(&character.id, b"hat_key"), 0);
        assert!(df::exists_(&character.id, b"mustache_key"), 1);

        // 帽子の色を変更
        let hat: &mut Hat = df::borrow_mut(&mut character.id, b"hat_key");
        hat.color = 0x00FF00;

        // キャラクターから帽子と口ひげを削除
        let hat: Hat = df::remove(&mut character.id, b"hat_key");
        let mustache: Mustache = df::remove(&mut character.id, b"mustache_key");

        // 帽子と口ひげがもはやキャラクターに添付されていないことを確認
        assert!(!df::exists_(&character.id, b"hat_key"), 0);
        assert!(!df::exists_(&character.id, b"mustache_key"), 1);

        sui::test_utils::destroy(character);
        sui::test_utils::destroy(mustache);
        sui::test_utils::destroy(hat);
    }
}

上の例では、Character オブジェクトと、ベクターに一緒に入れることができない2つの異なるタイプのアクセサリーを定義しています。しかし、ダイナミックフィールドを使用すると、これらを1つのオブジェクトに一緒に格納することができます。両方のオブジェクトは vector<u8> (バイト文字列リテラル)を介して Character に添付され、それぞれのキーを使用してアクセスできます。

ご覧のように、アクセサリーを Character に添付する際、値で 渡しました。言い換えれば、両方の値は新しいスコープに移動され、その所有権は Character オブジェクトに移転されました。Character オブジェクトの所有権を変更した場合、アクセサリーも一緒に移動されます。

そして、ダイナミックフィールドの最後の重要な特性として強調すべきは、それらが 親を通じてアクセスされる ということです。これは、HatMustache オブジェクトが直接アクセス可能ではなく、親オブジェクトと同じルールに従うことを意味します。

外部の型をダイナミックフィールドとして使用

ダイナミックフィールドにより、オブジェクトは他のモジュールで定義された型を含む、任意の型のデータを保持することができます。これは、ダイナミックフィールドの汎用的な性質と、型パラメータに対する比較的弱い制約のおかげです。これを説明するために、Character オブジェクトにいくつかの異なる値を添付してみましょう。

let mut character = Character { id: object::new(ctx) };

// `vector<u8>` 名を介して `String` を添付
df::add(&mut character.id, b"string_key", b"Hello, World!".to_string());

// `u32` 名を介して `u64` を添付
df::add(&mut character.id, 1000u32, 1_000_000_000u64);

// `bool` 名を介して `bool` を添付
df::add(&mut character.id, true, false);

この例では、ダイナミックフィールドの 名前 の両方に異なる型を使用できることを示しました。Stringvector<u8> 名を介して添付され、u64u32 名を介して添付され、boolbool 名を介して添付されています。ダイナミックフィールドでは何でも可能です!

孤立したダイナミックフィールド

孤立したダイナミックフィールドを防ぐために、Bag のような動的コレクション型を使用してください。これらはダイナミックフィールドを追跡し、添付されたフィールドがある場合はアンパッキングを許可しません。