dynamic_object_field

模块说明

  • dynamic_object_field(动态对象字段)特性同dynamic_field(动态字段)
  • 与之不同的是值必须具备key能力,对象仍然能从外部通过 UID 进行访问

源码路径

dynamic_object_field.move

方法图解

方法说明

分类方法说明
add<...>(object: &mut UID, name: Name, value: Value)向对象object添加名为name的值为value的动态字段
remove<...>(object: &mut UID, name: Name): Value 从对象object中删除名为name的动态字段,若不存在将会报错
borrow_mut<...>(object: &mut UID, name: Name): &mut Value从对象object中获取名为name的动态字段的可变引用,以便进行对动态字段的修改
borrow<...>(object: &UID, name: Name): &Value从对象object中获取名为name的动态字段的只读引用,用于进行信息查看
exists_<...>(object: &UID, name: Name): bool若对象object中存在名为name的动态字段则返回true,无需指定value类型
exists_with_type<...>(object: &UID, name: Name): bool若对象object中存在名为name的动态字段则返回true,需指定value类型
id<Name: copy + drop + store>(object: &UID, name: Name,): Option<ID>返回动态对象字段中的对象ID

代码示例

同样采用书架(Bookshelf)和书本(Book)的示例,书本对象作为动态字段添加到书架上,其中书本需要具备key能力,该对象可以从外部通过 UID 进行访问。

结构定义

注:Book结构需要具备key能力,结构体中也必须具备UID

public struct Bookshelf has key {
    id: UID,
    book_count: u64
}

public struct Book has key, store {
    id: UID,
    title: String,
    description: String,
}

创建书架共享对象

// 创建书架共享对象
public fun create_bookshelf(ctx: &mut TxContext) {
    transfer::share_object(Bookshelf {
        id: object::new(ctx),
        book_count: 0,
    });
}

添加书本到书架

调用dynamic_field::add方法。

// 添加书本到书架
public fun add_book(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>, ctx: &mut TxContext) {
    let book = Book {
        id: object::new(ctx),
        title: ascii::string(title),
        description: ascii::string(description)
    };

    dynamic_object_field::add<vector<u8>, Book>(&mut bookshelf.id,title, book);
    bookshelf.book_count = bookshelf.book_count + 1;
}

public fun add_book_obj(bookshelf: &mut Bookshelf, book: Book) {
    dynamic_object_field::add<vector<u8>, Book>(&mut bookshelf.id,
        book.title.into_bytes(), book);
    bookshelf.book_count = bookshelf.book_count + 1;
}

获取书本

调用dynamic_field::borrow方法。

// 获取书本
public fun get_book(bookshelf: &Bookshelf, title: vector<u8>): &Book {
    dynamic_object_field::borrow(&bookshelf.id, title)
}

设置书本的描述信息

调用dynamic_field::borrow_mut方法。

// 设置书本的描述信息
public fun set_book_desc(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
    let book_mut_ref: &mut Book = dynamic_object_field::borrow_mut(&mut bookshelf.id, title);
    book_mut_ref.description = ascii::string(description);
}

判断书本是否存在

调用dynamic_field::exists_dynamic_field::exists_with_type方法。

public fun is_book_existed(bookshelf: &Bookshelf, title: vector<u8>): bool {
    dynamic_object_field::exists_(&bookshelf.id, title)
}

public fun is_book_exists_with_type(bookshelf: &Bookshelf, title: vector<u8>): bool {
    dynamic_object_field::exists_with_type<vector<u8>, Book>(&bookshelf.id, title)
}

从书架上移除书本

调用dynamic_field::removedynamic_field::remove_if_exists方法。

// 从书架上移除书本
public fun remove_book(bookshelf: &mut Bookshelf, title: vector<u8>): Book {
    bookshelf.book_count = bookshelf.book_count - 1;
    dynamic_object_field::remove<vector<u8>, Book>(&mut bookshelf.id, title)
}

完整代码

  • dynamic_object_field
module cookbook::dynamic_object_field{
    use std::ascii::{Self, String};
    use sui::dynamic_object_field;

    public struct Bookshelf has key {
        id: UID,
        book_count: u64
    }

    public struct Book has key, store {
        id: UID,
        title: String, 
        description: String,
    }

    // 创建书架共享对象
    public fun create_bookshelf(ctx: &mut TxContext) {
        transfer::share_object(Bookshelf {
            id: object::new(ctx),
            book_count: 0,
        });
    }

    // 放置书本到书架
    public fun add_book(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>, ctx: &mut TxContext) {
        let book = Book {
            id: object::new(ctx),
            title: ascii::string(title),
            description: ascii::string(description)
        };

        dynamic_object_field::add<vector<u8>, Book>(&mut bookshelf.id,title, book); 
        bookshelf.book_count = bookshelf.book_count + 1;
    }

    public fun add_book_obj(bookshelf: &mut Bookshelf, book: Book) {
        dynamic_object_field::add<vector<u8>, Book>(&mut bookshelf.id,
            book.title.into_bytes(), book); 
        bookshelf.book_count = bookshelf.book_count + 1;
    }

    // 拿取书本
    public fun get_book(bookshelf: &Bookshelf, title: vector<u8>): &Book {
        dynamic_object_field::borrow(&bookshelf.id, title)
    }

    // 设置书本的描述信息
    public fun set_book_desc(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
        let book_mut_ref: &mut Book = dynamic_object_field::borrow_mut(&mut bookshelf.id, title);
        book_mut_ref.description = ascii::string(description);
    }

    // 判断书本是否存在
    public fun is_book_existed(bookshelf: &Bookshelf, title: vector<u8>): bool {
        dynamic_object_field::exists_(&bookshelf.id, title)
    }

    public fun is_book_exists_with_type(bookshelf: &Bookshelf, title: vector<u8>): bool {
        dynamic_object_field::exists_with_type<vector<u8>, Book>(&bookshelf.id, title)
    }

    // 从书架上移除书本
    public fun remove_book(bookshelf: &mut Bookshelf, title: vector<u8>): Book {
        bookshelf.book_count = bookshelf.book_count - 1;
        dynamic_object_field::remove<vector<u8>, Book>(&mut bookshelf.id, title)
    }

    public fun get_book_count(bookshelf: &Bookshelf): u64{
        bookshelf.book_count
    }

    public fun get_book_title(book: &Book): String {
        book.title
    }

    public fun get_book_desc(book: &Book): String {
        book.description
    }
}
  • dynamic_field_tests
#[test_only]
module cookbook::dynamic_object_field_tests {
    use std::ascii;
    use sui::test_scenario as ts;
    use cookbook::dynamic_object_field::{Bookshelf, create_bookshelf, add_book, get_book, 
        set_book_desc, is_book_existed, is_book_exists_with_type, remove_book,
        get_book_count, get_book_title, get_book_desc};

    #[test_only]
    use sui::test_utils::assert_eq;

    #[test]
    public fun test_dynamic_object_field() {
        let alice = @0xa;    

        let mut ts = ts::begin(alice);

        // 创建书架
        {
            create_bookshelf(ts.ctx());
        };

        // 放置书本到书架
        let expected_title = b"Mastering Bitcoin";
        let expected_description= b"1st Edition";
        let expected_new_description= b"3rd Edition";

        {
            ts.next_tx(alice);
            let mut bookshelf: Bookshelf = ts.take_shared();

            add_book(
                &mut bookshelf, 
                expected_title, 
                expected_description,
                ts.ctx(),
            );

            assert_eq(bookshelf.get_book_count(), 1);

            ts::return_shared(bookshelf);
        };

        // 拿取书本
        {
            ts.next_tx(alice);
            let bookshelf: Bookshelf = ts.take_shared();

            let book = get_book(
                &bookshelf, 
                expected_title, 
            );

            assert_eq(book.get_book_title(), ascii::string(expected_title));
            assert_eq(book.get_book_desc(), ascii::string(expected_description));

            ts::return_shared(bookshelf);
        };

        // 设置书本的描述信息
        {
            ts.next_tx(alice);
            let mut bookshelf: Bookshelf = ts.take_shared();

            set_book_desc(
                &mut bookshelf, 
                expected_title, 
                expected_new_description,
            );

            let book = get_book(
                &bookshelf, 
                expected_title, 
            );

            assert_eq(book.get_book_title(), ascii::string(expected_title));
            assert_eq(book.get_book_desc(), ascii::string(expected_new_description));

            ts::return_shared(bookshelf);
        };

        // 判断书本是否存在
        {
            ts.next_tx(alice);
            let bookshelf: Bookshelf = ts.take_shared();

            let is_existed = is_book_existed(
                &bookshelf, 
                expected_title, 
            );
            assert_eq(is_existed, true);

            let is_existed = is_book_exists_with_type(
                &bookshelf, 
                expected_title, 
            );
            assert_eq(is_existed, true);

            ts::return_shared(bookshelf);
        };

        // 从书架上移除书本
        {
            ts.next_tx(alice);
            let mut bookshelf: Bookshelf = ts.take_shared();

            assert_eq(bookshelf.get_book_count(), 1);

            let book = remove_book(
                &mut bookshelf, 
                expected_title, 
            );

            assert_eq(bookshelf.get_book_count(), 0);
            assert_eq(book.get_book_title(), ascii::string(expected_title));
            assert_eq(book.get_book_desc(), ascii::string(expected_new_description));

            bookshelf.add_book_obj(book);

            ts::return_shared(bookshelf);
        };

        ts.end();
    }
}