动态字段迁移指南

动态字段是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);
    /* ... */
}

进一步了解动态字段