动态字段

在之前的课程中,我们深入探讨了对象,并创建了具有自身属性的 SuiFrens 对象以及告诉 Web 界面如何显示它们的 display 对象。 我们还讨论了以下主题:

  1. 嵌套结构体:具有存储能力的结构体可以嵌入到另一个结构体中。这通常用于非对象结构体,以将长列表的字段划分为逻辑组件。
  2. 对象包装:将一个 SuiFren 放入另一个对象中。这也使用了存储能力,但实际上从存储中移除了被包装的对象。 被包装到 GiftBoxes 中的 SuiFren 在按对象搜索时将不再被查找。

在本课中,我们将讨论另一种将结构体和对象组合在一起的方法——动态字段。您可以将动态字段视为未在对象结构体上显式定义的“隐形”字段。 假设我们有一个具有以下初始属性的 Laptop 对象:

public struct Laptop has key {
    id: UID,
    screen_size: u64,
    model: u64,
}

假设我们想在将来为这台笔记本电脑动态添加更多的属性,但还不知道这些属性到底是什么。 我们不能立即在结构体中定义它们。这时动态字段就派上用场了——你不需要知道所有要添加到对象的字段,因为这些属性可以作为动态字段添加!

use sui::dynamic_field;

public fun add_attribute(laptop: &mut Laptop, name: String, value: u64) {
    dynamic_field::add(&mut laptop.id, name, value);
}

为了给对象添加动态字段,您需要该对象的可变引用。请记住,对于归属对象,只有所有者可以发送使用其对象的可变/不可变引用或值的交易。

共享对象可以被任何人修改。添加动态字段可以简单地通过 dynamic_field::add,使用可变对象的 id、键和值来完成。

键和值可以是基本类型(数字、字符串等)或结构体。注意,作为键使用的结构体必须具有 copydropstore 能力, 而作为值使用的结构体必须具有 store 能力。

public struct StickerName has copy, drop, store {
    name: String,
}

public struct Sticker has store {
    image_url: String,
}

public fun add_sticker(laptop: &mut Laptop, name: String, image_url: String) {
    let sticker_name = StickerName { name };
    let sticker = Sticker { image_url };
    dynamic_field::add(&mut laptop.id, sticker_name, sticker);
}

动态字段的名称是独特的,所以你不能多次添加相同名称的字段。 要读取或修改动态字段,分别需要对象的不可变引用和可变引用以及字段名称(键)。 如果字段名称是一个结构体,你需要结构体的值本身,这就是为什么作为键使用的结构体必须具有 copydrop 能力的原因。

public fun read_image_url(laptop: &Laptop, name: String): String {
    let sticker_name = StickerName { name };
    let sticker_reference: &Sticker = dynamic_field::borrow(&laptop.id, sticker_name);
    sticker_reference.image_url
}

public fun set_image_url(laptop: &mut Laptop, name: String, new_url: String) {
    let sticker_name = StickerName { name };
    let sticker_mut_reference: &mut Sticker = dynamic_field::borrow_mut(&mut laptop.id, sticker_name);
    sticker_mut_reference.image_url = new_url;
}

请注意,通常你需要指定借用字段的类型(在上面的示例中为 &Sticker 和 &mut Sticker)。 你也可以使用可变对象引用和键来移除现有的动态字段:

public fun remove_sticker(laptop: &mut Laptop, name: String) {
    let sticker_name = StickerName { name };
    dynamic_field::remove(&mut laptop.id, sticker_name);
}

你可以使用 dynamic_field::exists_(&laptop.id, sticker_name) 来检查动态字段是否存在。