动态字段
在 Sui 中,动态字段允许您将任意数据附加到对象。这使它们成为构建需要灵活数据存储的应用程序的强大工具。动态字段就像键值对一样工作,其中键是 vector<u8>
,值可以是任何类型。这意味着您可以使用动态字段来存储各种数据,例如:
- 角色的物品清单
- 用户的个人资料信息
- 游戏中物品的属性
动态字段类型
Sui 中有两种动态字段:
dynamic_field
:用于将任意数据附加到对象。dynamic_object_field
:专门用于将对象附加到其他对象。
dynamic_field
dynamic_field
可用于将任何类型的数据附加到对象。这包括基元类型、结构体,甚至其他动态字段。要使用 dynamic_field
,您需要导入 sui::dynamic_field
模块,该模块通常使用别名 df
。
dynamic_object_field
dynamic_object_field
专门用于将对象附加到其他对象。它们只能存储具有 key
能力的对象。这是因为 dynamic_object_field
存储对象的 ID,而不是对象本身。要使用 dynamic_object_field
,您需要导入 sui::dynamic_object_field
模块,该模块通常使用别名 dof
或 ofield
。
使用动态字段
sui::dynamic_field
和 sui::dynamic_object_field
模块都提供了一组用于管理动态字段的函数。一些最常见的函数包括:
add
:向对象添加一个动态字段。remove
:从对象中移除一个动态字段。borrow
:从对象中借用一个动态字段。borrow_mut
:从对象中借用一个动态字段的可变引用。contains
:检查一个动态字段是否存在于对象中。id
:返回动态对象字段中存储的对象的 ID。
所有动态集合类型都支持 borrow
和 borrow_mut
方法的索引语法。如果您在示例中看到方括号,它们将转换为 borrow
和 borrow_mut
调用。
let hat: &Hat = &bag[b"key"];
let hat_mut: &mut Hat = &mut bag[b"key"];
// 等价于
let hat: &Hat = bag.borrow(b"key");
let hat_mut: &mut Hat = bag.borrow_mut(b"key");
动态字段示例
以下是如何使用 dynamic_field
将帽子和胡子附加到角色对象的示例:
use sui::dynamic_field as df;
use std::string::String;
// Struct representing a character to which dynamic fields are attached
public struct Character has key {
id: UID
}
// Different accessories that can be attached to the character
// They must have the `store` ability
public struct Hat has key, store {
id: UID,
color: u32
}
public struct Beard 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) };
// Attach a hat to the character's UID
df::add(
&mut character.id,
b"hat_key",
Hat { id: object::new(ctx), color: 0xFF0000 }
);
// Similarly, attach a beard to the character's UID
df::add(
&mut character.id,
b"beard_key",
Beard { id: object::new(ctx) }
);
// Verify the hat and beard are attached to the character
assert!(df::exists_(&character.id, b"hat_key"), 0);
assert!(df::exists_(&character.id, b"beard_key"), 1);
// Modify the color of the hat
let hat: &mut Hat = df::borrow_mut(&mut character.id, b"hat_key");
hat.color = 0x00FF00;
// Remove the hat and beard from the character
let hat: Hat = df::remove(&mut character.id, b"hat_key");
let beard: Beard = df::remove(&mut character.id, b"beard_key");
// Verify the hat and beard are no longer attached to the character
assert!(!df::exists_(&character.id, b"hat_key"), 0);
assert!(!df::exists_(&character.id, b"beard_key"), 1);
sui::test_utils::destroy(character);
sui::test_utils::destroy(beard);
sui::test_utils::destroy(hat);
}
在这个例子中,我们定义了一个 Character
对象和两种不同类型的配件。由于配件具有 store
能力,因此可以使用动态字段将它们存储在 Character
对象中。
孤儿对象
当您从父对象中删除动态字段时,附加到该字段的对象将成为孤儿对象。孤儿对象不再与任何对象关联,因此无法再访问。要避免创建孤儿对象,您应该在删除父对象之前删除所有动态字段。
以下是如何创建孤儿对象的示例:
let hat = Hat { id: object::new(ctx), color: 0xFF0000 };
let mut character = Character { id: object::new(ctx) };
// Attach a `Hat` using a `vector<u8>` key
df::add(&mut character.id, b"hat_key", hat);
// ! Do not do this in your code
// ! Dangerous - deleting the parent object
let Character { id } = character;
id.delete();
// ...`Hat` is now stranded and can never be accessed again
字段名称
动态字段的名称是 vector<u8>
。这意味着您可以使用任何字节字符串作为动态字段的名称。但是,在大多数情况下,您应该使用描述性名称来使您的代码更易于阅读。
以下是一些动态字段名称的示例:
b"name"
b"description"
b"items"
您还可以使用更复杂的结构体作为字段名称。例如,您可以使用以下结构体来表示物品的键:
struct ItemKey {
name: String,
id: u64,
}
最佳实践
以下是一些使用动态字段的最佳实践:
- 使用描述性名称来使您的代码更易于阅读。
- 在删除父对象之前删除所有动态字段,以避免创建孤儿对象。
- 谨慎使用动态字段。它们是一个强大的工具,但如果使用不当,它们会导致代码复杂且难以维护。
总结
动态字段是一个强大的工具,可以用来构建需要灵活数据存储的应用程序。通过理解动态字段的工作方式,您可以构建更强大和更通用的应用程序。
以上内容遵循最新的 Move 2024 语法规范,且代码部分仅保留注释为中文。