对象的字段在发布时就写死了
在 Sui 上,一个对象能被外部程序读写的字段,是它所属模块里用 struct 声明好的那几个,名字必须是 Move 的标识符,而且一旦合约发布,这个集合就固定下来。官方文档列出了几条硬限制:想往一个对象里塞很多别的对象,它会迅速变大,而对象有大小上限,越大交易越贵;想在一个字段里存一串类型各不相同的东西也不行,因为 Move 的 vector 只能装同一种类型。这些限制不是为了为难开发者,而是 Move 强类型与对象模型天然的约束。Dynamic Field(动态字段)就是 Sui 为绕开”字段固定”这堵墙提供的一把钥匙。
动态字段:按名字现场挂、现场摘
官方文档对它的描述是:可以挂任意命名的字段、能随增删变化,而且只有在被访问时才对 gas 产生影响。它绕开固定字段的做法不是把数据写进结构体本身,而是在这个对象下面挂出一个子对象,用一个由名字派生的哈希去寻址它——文档里的示例函数先算出名字与类型对应的哈希,再以这个哈希新建一个子对象登记在父对象下。要读某个动态字段,就得带着这个名字去算哈希再查,这也是”只在访问时计 gas”的原因:没被访问的动态字段不会平白增加读写成本。

两种字段,差别在能不能被外部看见
这里有个容易被忽略又很重要的分界。文档把动态字段分成两类:一类叫字段(dynamic_field),能存任何带 store 能力的值,但被这样存进去的对象算作被包裹(wrapped),浏览器、钱包这类外部工具没法直接拿它的 ID 去查它;另一类叫对象字段(dynamic_object_field),存的必须是带 key、且第一个字段是 UID 的对象,好处是这些对象仍能在自己的 ID 上被外部工具访问。翻译成一句话:如果你希望挂出去的东西还能在区块浏览器里被单独搜到、能被别的程序独立引用,就要用对象字段;如果只是内部账本、不在乎外部可见性,普通字段更省事。对靠浏览器或钱包查账的用户来说,这直接决定了”为什么有的资产查得到 ID、有的查不到”——不是丢了,而是被包裹进了父对象。
命名规则与它意味着什么
动态字段的名字不必是标识符,文档写明它可以是任何同时带 copy、drop、store 能力的值:整数、布尔、字节串,以及内容全部满足这三个能力的自定义结构都可以当名字。灵活性很高,但也意味着名字本身就是数据的一部分:同一段字节用不同类型(比如 u8 和 u64)当键,会派生出不一样哈希,落到不同的子对象上。开发者在约定命名时要连类型一起定死,否则读写两侧对不齐就会”字段明明加了却读不到”。这类坑不体现在余额上,排查时往往要看框架层的哈希逻辑,而不是只看转账记录。
用户能观察到的边界
从链上观察的视角,动态字段改变不了 Sui 对象模型的根:每个对象有唯一 ID、有所有者、有版本,父对象通过子对象关系把它们串起来。它带来的实际差别是——一个逻辑上很大的集合可以被拆成一堆按需访问的小对象,避免单个对象膨胀到超过大小上限、把 gas 顶高;代价是遍历一个动态表通常要多次访问、每次各自计费,不像读一个定长数组那样一次到位。对读账的人来说,“某个地址下的动态字段清单”是可以在浏览器或 RPC 里按父对象 ID 枚举的,但字段值是否可见,取决于它被存成字段还是对象字段。理解这层区别,比把 Sui 简单类比成”另一种智能合约账本”要准确得多。
风险提示:本文为机制说明,不构成投资建议;能力(copy、drop、store、key)与包裹可见性是 Move 与框架层规则,可能随版本演进,请以 Sui 官方文档为准。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。