sui-move进阶:dynamic_field上一节我们说到,table模块的实现用到了dynamic_field,而我将在这一节针对该库进行进一步的讲解。0x2::dynamic_field模块为Sui对象提供了动态字段功能。这允许开发者在对象构造后动态地添加字段,而不是仅依赖静态声明
上一节我们说到,table模块的实现用到了dynamic_field,而我将在这一节针对该库进行进一步的讲解。
0x2::dynamic_field
模块为 Sui 对象提供了动态字段功能。这允许开发者在对象构造后动态地添加字段,而不是仅依赖静态声明的字段定义。
动态字段为对象扩展提供了极大的灵活性,并且是实现核心集合类型的基础模块之一。
动态命名:
动态字段的名称可以是任何具有 copy
、drop
和 store
能力的值,例如整数、布尔值或字符串。
与静态字段不同,动态字段的名称无需预先声明。
基于对象系统的存储:
动态字段通过 Sui 的对象系统存储和管理。
每个动态字段由一个唯一标识符(UID
)管理,其值是基于对象 ID、字段名称和值类型的哈希生成。
Field
结构动态字段的核心数据结构如下:
struct Field<Name: copy, drop, store, Value: store> has key {
id: object::UID, // 动态字段的唯一标识符
name: Name, // 字段名称
value: Value, // 字段值
}
能力要求:
字段名称(Name):必须具有 copy、drop 和 store 能力。
字段值(Value):必须具有 store 能力。
以下是 dynamic_field 模块中提供的主要功能:
函数原型
/// Adds a dynamic field to the object `object: &mut UID` at field specified by `name: Name`.
/// Aborts with `EFieldAlreadyExists` if the object already has that field with that name.
public fun add<Name: copy + drop + store, Value: store>(
// we use &mut UID in several spots for access control
object: &mut UID,
name: Name,
value: Value,
) {
let object_addr = object.to_address();
let hash = hash_type_and_key(object_addr, name);
assert!(!has_child_object(object_addr, hash), EFieldAlreadyExists);
let field = Field {
id: object::new_uid_from_hash(hash),
name,
value,
};
add_child_object(object_addr, field)
}
该函数的功能是:
EFieldAlreadyExists
错误。相信还有人好奇,这个函数所调用的两个函数:hash_type_and_key
和add_child_object
又是个什么情况呢?
答:
它们是move的native函数,由runst实现,如果可能的话,我后续将进行Sui库的分析与解读,并写出教程。
示例
let obj_id = ...; // 对象的 UID
dynamic_field::add(&mut obj_id, "name", "Alice");
函数原型
/// Immutably borrows the `object`s dynamic field with the name specified by `name: Name`.
/// Aborts with `EFieldDoesNotExist` if the object does not have a field with that name.
/// Aborts with `EFieldTypeMismatch` if the field exists, but the value does not have the specified
/// type.
public fun borrow<Name: copy + drop + store, Value: store>(object: &UID, name: Name): &Value {
let object_addr = object.to_address();
let hash = hash_type_and_key(object_addr, name);
let field = borrow_child_object<Field<Name, Value>>(object, hash);
&field.value
}
该函数的功能是:
- 返回指定字段名称的不可变引用。
- 如果字段不存在,则中止并抛出 EFieldDoesNotExist。
- 如果字段的类型与指定类型不匹配,则中止并抛出 `EFieldTypeMismatch`。
示例:
let name = dynamic_field::borrow(&obj_id, "name");
assert!(*name == "Alice");
函数原型
/// Mutably borrows the `object`s dynamic field with the name specified by `name: Name`.
/// Aborts with `EFieldDoesNotExist` if the object does not have a field with that name.
/// Aborts with `EFieldTypeMismatch` if the field exists, but the value does not have the specified
/// type.
public fun borrow_mut<Name: copy + drop + store, Value: store>(
object: &mut UID,
name: Name,
): &mut Value {
let object_addr = object.to_address();
let hash = hash_type_and_key(object_addr, name);
let field = borrow_child_object_mut<Field<Name, Value>>(object, hash);
&mut field.value
}
该函数的功能是:
- 返回指定字段名称的可变引用。
- 如果字段不存在或类型不匹配,则行为与 borrow 类似。
换言之,它就是borrow
函数的mut版本,对于是否可以返回mut引用,请回忆或者回看我已完成的引用教程。
示例
let name_mut = dynamic_field::borrow_mut(&mut obj_id, "name");
*name_mut = "Bob"; // 修改字段值
函数原型
/// Removes the `object`s dynamic field with the name specified by `name: Name` and returns the
/// bound value.
/// Aborts with `EFieldDoesNotExist` if the object does not have a field with that name.
/// Aborts with `EFieldTypeMismatch` if the field exists, but the value does not have the specified
/// type.
public fun remove<Name: copy + drop + store, Value: store>(object: &mut UID, name: Name): Value {
let object_addr = object.to_address();
let hash = hash_type_and_key(object_addr, name);
let Field { id, name: _, value } = remove_child_object<Field<Name, Value>>(object_addr, hash);
id.delete();
value
}
该函数的功能是:
- 移除指定字段并返回其值。
- 如果字段不存在或类型不匹配,则行为与 borrow 类似。
示例:
let removed_value = dynamic_field::remove(&mut obj_id, "name");
assert!(removed_value == "Alice");
检查名称(Name)
是否存在
/// Returns true if and only if the `object` has a dynamic field with the name specified by
/// `name: Name` but without specifying the `Value` type
public fun exists_<Name: copy + drop + store>(object: &UID, name: Name): bool {
let object_addr = object.to_address();
let hash = hash_type_and_key(object_addr, name);
has_child_object(object_addr, hash)
}
无需过多解释。
/// Returns true if and only if the `object` has a dynamic field with the name specified by
/// `name: Name` with an assigned value of type `Value`.
public fun exists_with_type<Name: copy + drop + store, Value: store>(
object: &UID,
name: Name,
): bool {
let object_addr = object.to_address();
let hash = hash_type_and_key(object_addr, name);
has_child_object_with_ty<Field<Name, Value>>(object_addr, hash)
}
在检查动态字段是否存在之外,还检查其是否匹配(Name与Value匹配)。
示例
let exists = dynamic_field::exists_(&obj_id, "name");
assert!(exists);
let exists_with_type = dynamic_field::exists_with_type<&str>(&obj_id, "name");
assert!(exists_with_type);
函数原型
/// Removes the dynamic field if it exists. Returns the `some(Value)` if it exists or none otherwise.
public fun remove_if_exists<Name: copy + drop + store, Value: store>(
object: &mut UID,
name: Name,
): Option<Value> {
if (exists_<Name>(object, name)) {
option::some(remove(object, name))
} else {
option::none()
}
}
该函数的功能是:
- 如果字段存在,则移除并返回 Option::some 包裹的值。
- 如果字段不存在,则返回 Option::none。
示例:
match dynamic_field::remove_if_exists(&mut obj_id, "name") {
option::some(value) => debug::print(&value),
option::none => debug::print("Field does not exist"),
}
常量 | 错误码 | 描述 |
---|---|---|
EFieldAlreadyExists | 0 | 动态字段已存在 |
EFieldDoesNotExist | 1 | 动态字段不存在 |
EFieldTypeMismatch | 2 | 字段存在但类型不匹配 |
EBCSSerializationFailure | 3 | 序列化失败 |
ESharedObjectOperationNotSupported | 4 | 尝试将共享对象添加为动态字段时发生错误 |
字段唯一标识符(UID):
动态字段存储:
动态扩展对象:
在运行时向对象添加新属性,而无需修改类型定义。 实现集合类型:
作为核心集合类型(如 Table)的基础模块。
灵活的存储与查询:
0x2::dynamic_field 模块提供了一种灵活的方式扩展 Sui 对象的功能:
掌握动态字段的使用,是构建复杂智能合约的关键技能。
许多高级的合约和代码,其实都应用了dynemic_field这样的底层库,了解和理解这些底层库对于进一步掌握Sui Move编程是必要的。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!