对象拥有对象
另一种组合对象的方法是让对象拥有其他对象。对于其他三种方法——对象包装、动态字段、动态对象字段,所有权被放弃或设置为隐藏对象(动态字段对象)。在某些情况下,希望父对象拥有子对象(例如,SuiFren 拥有它所戴的帽子)。这可以被视为在复杂应用设计中明确表示对象层次结构的一种方法,其中包含多种类型的对象。
对象拥有对象的另一个有趣用例是可组合的NFT——就像 SuiFren 拥有它们的帽子一样。这允许将 NFT 强有力地组合在一起,使其对用户而言是自然的,这在使用 NFT 的 Web 3 游戏中经常见到。 与其他三种方法相比,对象拥有对象的一个缺点是以后很难移除被拥有的对象。因此,如果关系很少或从未改变,对象拥有对象通常更有用。
public struct Laptop has key {
id: UID,
}
public struct Sticker has key, store {
id: UID,
image_url: String,
}
public fun add_sticker(laptop: &Laptop, sticker: Sticker) {
transfer::public_transfer(sticker, object::uid_to_address(&laptop.id));
}
在上面的例子中,我们显式地将 Sticker 对象转移到 Laptop。它现在由 Laptop 拥有。为了以后移除它,我们需要使用 Receiving
public fun remove_sticker(laptop: &mut Laptop, sticker: Receiving<Sticker>) {
let sticker = transfer::public_receive(&mut laptop.id, sticker);
// 用Sticker做点什么
}
为了提取先前转移到 Laptop 对象中的 Sticker 对象,我们需要 Laptop 的所有者调用 remove_sticker
函数,传入 Laptop 的可变引用和一个类型为 Receivingtransfer::public_transfer
来获取底层的 Sticker。如你所见,这比对象包装或动态字段要复杂一些。
请注意,从另一个对象中提取对象也受转移政策的约束(参见前一课程的相应章节):
- 如果内部对象具有存储能力,可以调用
transfer::public_receive
将其从定义其结构体的模块之外提取。 - 如果内部对象没有存储能力,不能调用
transfer::public_receive
。只能在定义其结构体的模块内部调用transfer::receive
。