对象数据结构 - 一袋混合对象
在上一节课中,我们介绍了如何将 SuiFren 对象包装到 GiftBox 对象中。
public struct GiftBox has key {
id: UID,
inner: SuiFren,
}
entry fun wrap_fren(fren: SuiFren, ctx: &mut TxContext) {
let gift_box = GiftBox {
id: object::new(ctx),
inner: fren,
};
transfer::transfer(gift_box, tx_context::sender(ctx));
}
假设我们希望让 GiftBox 变得更加令人惊喜,允许打开它的用户可能会收到 1 到 5 个 Frens。你可能会倾向于创建 5 个 SuiFren 字段:
public struct GiftBox has key {
id: UID,
inner_1: SuiFren,
inner_2: SuiFren,
inner_3: SuiFren,
inner_4: SuiFren,
inner_5: SuiFren,
}
这看起来很乱,而且实际上并不有效!这将迫使 GiftBox 总是包含恰好 5 个 SuiFren。 如果我们还允许 GiftBox 为空,可能不包含任何 SuiFren,该怎么办?更好的方法是使用向量:
public struct GiftBox has key {
id: UID,
frens: vector<SuiFren>,
}
这简洁多了!现在每个 GiftBox 可以包含任意数量的 SuiFren,包括零(空向量)。 但如果我们想让它更有趣一些——每个 GiftBox 也可以包含一些帽子呢?我们可以创建第二个帽子向量 。 但你看到了问题所在。如果我们希望 GiftBox 包含不同类型对象的混合集合, 向量在这里不起作用,因为它只能包含单一类型的对象。更好的方法是使用对象袋(Object Bag)!
use sui::object_bag::{Self, ObjectBag};
public struct MyBag has key {
id: UID,
object_bag: ObjectBag,
}
public fun create_bag(ctx: &mut TxContext) {
transfer::transfer(MyBag {
id: object::new(ctx),
object_bag: object_bag::new(ctx),
}, tx_context::sender(ctx));
}
public fun add_to_bag<SomeObject>(my_bag: &mut MyBag, key: String, object: SomeObject) {
object_bag::add(&mut my_bag.object_bag, key, object);
}
在底层,对象袋(ObjectBag)使用动态对象字段来存储不同类型的对象。
语法与动态字段非常相似——要向袋子中添加对象,你需要一个原始类型的键(例如 u64 类型的索引)或具有 copy
/drop
/store
能力的结构体,
以及一个具有 key
能力(对象结构体)的结构体类型值。
object_bag 模块还提供其他函数,例如 borrow
、borrow_mut
、contains
(类似于 dynamic_field::exists_)、
length
(返回袋子中的对象数量)和 is_empty
。