第二卷 · 第 03 章 · ★★★
官方源码:object.move
配套示例:examples/tests/v2_object_transfer_tests.move
- 能区分
UID与ID。 - 知道
has key对象为什么必须有id: UID。 - 能解释对象创建、读取 ID、删除的完整生命周期。
一个普通 Sui 对象通常长这样:
public struct Note has key {
id: UID,
text: String,
}这段代码里有三个关键点:
has key:说明它可以成为链上对象根。id: UID:说明它有唯一对象身份。id是首字段:这是 Sui 对象模型约定。
没有 UID,它只是普通 Move 值;有 UID 且 has key,它才能进入对象存储。
| 项目 | UID |
ID |
|---|---|---|
| 是否可复制 | 不可复制 | 可复制 |
| 是否可丢弃 | 不可随便丢弃 | 可丢弃 |
| 代表什么 | 对象身份本体 | 对象地址句柄 |
| 常见用途 | 对象字段、删除时消费 | 事件、索引、引用 |
| 是否等于权限 | 持有对象才有操作权 | 只是门牌,不是钥匙 |
最重要的比喻:
UID = 地契
ID = 门牌复印件
别人知道你的对象 ID,不代表能修改你的对象。
对象创建通常是:
let id = object::new(ctx);
let obj = Note { id, text };
transfer::transfer(obj, tx_context::sender(ctx));学习重点:
object::new(ctx)创建UID。ctx确保新对象地址来自本笔交易。- 创建后必须处理对象:转移、共享、冻结或删除。
- 不能创建后让资源悬空。
如果要销毁对象,必须消费 UID:
let Note { id, text } = note;
object::delete(id);如果对象里还有不能丢弃的资源,例如 Balance<T>、Coin<T>、Table<K,V>,必须先处理它们:
- 提款。
- 清空。
- 销毁零值。
destroy_empty。- 转移给别人。
删除对象不是“把 struct 丢掉”,而是按资源规则逐个收尾。
Sui Framework 中有一些固定 ID 的系统对象:
| 对象 | 典型 ID | 用途 |
|---|---|---|
Clock |
0x6 |
链上时间 |
Random |
0x8 |
链上随机 |
SystemState |
0x5 |
系统状态,本书不深讲 |
DenyList 等 |
见源码 | 策略与合规路径 |
应用代码通常通过类型化对象使用它们,而不是到处手写十六进制地址。
- 把
ID当作权限。 - 创建对象后没有转移或删除。
- 删除对象时忘记
object::delete(id)。 - 对象里有非
drop字段却试图直接丢弃。 - 在事件里记录了 ID,就以为对象一定还存在。
事件只是历史记录;对象是否还存在,要看链上状态。
写一个 Note 对象:
create(text, ctx):创建并转给 sender。update(&mut Note, new_text, ctx):只有 owner 或字段 owner 可以改。delete(Note):消费对象并删除UID。- 测试 Alice 创建、修改、删除。
- 测试 Bob 不能操作 Alice 的对象。
UID 是对象身份本体,ID 是可复制引用。Sui 对象模型的安全性来自:身份不能伪造,资源不能随便丢弃,所有权变化必须走明确路径。
- 为什么
ID可以复制而UID不行? - 删除对象时为什么必须调用
object::delete? - 如果对象里有
Balance<SUI>,删除前要做什么? - 事件里的对象 ID 能否证明对象仍然存在?