动态字段迁移指南
动态字段是Sui Move 的一个新概念,它取代了子对象,创造了更大的编程灵活性。
随着Sui 0.13.0我们将用动态字段取代子对象的概念。这是一个 变革中与子对象交互的Move API。 sui::transfer
(传输到对象
和 transfer_too_object_id
) 删除,代之以 sui动态字段
和 sui动态对象字段
用于处理动态字段。
为避免在使用子对象的应用程序中出现错误,请遵循以下迁移指南,并准备好在 Devnet 更新至 0.13.0 时登陆更改(观看 #devnet-updates上的公告)。为了尽量减少应用程序的停机时间,请等到 Devnet 更新后再进行更改,因为在此之前,与动态字段交互的新 API 将不可用。
什么是动态字段?
动态字段是子对象的一种概括。动态字段允许Move 开发人员在Sui 上使用新数据对结构体进行即时扩展。
他们 解决 "儿童对象 "的主要问题 需要将其祖先所有权链(其父对象、父对象的父对象,依此类推,直到发送者拥有的根对象)通过 条目
功能。这种限制使处理嵌套较深的子对象变得非常麻烦,而且不支持在事务开始时无法知道祖先链的使用(即动态加载子对象)。使用动态字段后,只有根对象会被传递到 条目
函数,其动态字段可在事务执行过程中即时访问。(事实上,将动态字段值作为输入传递给入口函数现在会失败)。
它们还有一些其他改进:
- 持有任何
store
值,而不仅仅是对象。 - 按名称标记和查找字段(名称可以是任何
抄袭
,滴剂
,store
值)。 - 支持新的链上键值存储,如
表格
(同质键和值)和袋子
(异质键和值)。
疯狂地使用它们!我们听到过很多人因为缺少数据结构而无法在Move 中实现它们而感到沮丧。现在,您应该可以在Move 中高效地实现各种数据结构!无论是链接哈希表、前缀三元组还是简单的链接列表。动态字段确实需要读/写对象到存储空间。对于数量很少的项目,我们仍然希望简单的向量比复杂的数据结构更高效。
目前的限制
我们仍在积极开发动态字段的某些部分,但我们迫不及待地想与您分享这一功能,看看您能用它创建什么,并获得您的反馈。在使用该功能时,请注意以下已知问题,这些问题将在未来的版本中得到解决:
去除
的动态字段目前未经优化,无法获得全额存储退款。在引擎盖下动态字段
(和动态对象字段
) 创建现场
对象来存储其键值对。- 目前没有
存在_
用于动态字段
来检查对象上是否已经定义了具有给定名称的字段。 - 动态字段对象存在一些潜在的耐久性/一致性问题:如果验证器在处理带有动态字段的事务时宕机,然后又重新启动,则可能无法继续处理带有这些对象的事务。我们正在积极研究解决这个问题的办法。
迁移指南
下面的建议可以将现有的子对象转换为使用动态字段,但请注意,在许多情况下,您可能需要重新考虑 API 的用法。在许多情况下,代码可以变得更简单或对最终用户更友好。下面的示例旨在帮助您进行直接替换,以帮助运行。
替换调用 传输到对象
与 增加
// Old, Child Object API
use sui::transfer;
transfer::transfer_to_object(&mut parent, child)
// New, Dynamic Field API
use sui::dynamic_object_field as ofield;
let id = object::id(&child);
ofield::add(&mut parent.id, id, child);
新的应用程序接口为 父母
与 孩子
的 ID 作为名称,而 孩子
作为值。
替换 条目
调用的乐趣 借用
, borrow_mut
或 去除
(注意:此建议假定您已按照之前的建议从 传输到对象
至 ofield::add
use 孩子
的 ID 作为字段名)。
// Child, Parent, GrandParent are all Objects
struct Child has key, store { id: UID, /* ... */ }
struct Parent has key, store { id: UID, /* ... */ }
struct GrandParent has key, store { id: UID, /* ... */ }
// Old, Child Object API
entry fun read_child(gp: &GrandParent, p: &Parent, c: &Child) {
/* ... */
}
entry fun write_child(gp: &mut GrandParent, p: &mut Parent, c: &mut Child) {
/* ... */
}
entry fun take_child(gp: &mut GrandParent, p: &mut Parent, c: Child) {
/* ... */
}
// New, Dynamic Field API
use sui::dynamic_object_field as ofield;
entry fun read_child(gp: &GrandParent, pid: ID, cid: ID) {
let p = ofield::borrow<ID, Parent>(&gp.id, pid);
let c = ofield::borrow<ID, Child>(&p.id, cid);
/* ... */
}
entry fun write_child(gp: &mut GrandParent, pid: ID, cid: ID) {
let p = ofield::borrow_mut<ID, Parent>(&mut gp.id, pid);
let c = ofield::borrow_mut<ID, Child>(&mut p.id, cid);
/* ... */
}
entry fun take_child(gp: &mut GrandParent, pid: ID, cid: ID) {
let p = ofield::borrow_mut<ID, Parent>(&mut gp.id, pid);
let c = ofield::remove<ID, Child>(&mut p.id, cid);
/* ... */
}