bag
模块说明
Bag
同样是一种类似于map
的集合- 与
Table
的同构映射不同,Bag
是异构映射集合,可以容纳任意类型的键值对 - 与
Table
类似,它的键和值不是存储在Bag
值中,而是使用Sui
的对象系统(sui::dynamic_field
)进行存储 Bag
结构仅作为一个句柄,用于在对象系统中检索这些键和值- 具有完全相同的键值映射的
Bag
值不会被==
判断为相等 Bag
必须为空才能被销毁
源码路径
方法图解
结构定义
Bag
类型没有任何类型参数,可以使用任意类型作为键值。
public struct Bag has key, store {
/// the ID of this bag
id: UID,
/// the number of key-value pairs in the bag
size: u64,
}
方法说明
分类 | 方法 | 说明 |
---|---|---|
初始化 | new(ctx: &mut TxContext): Bag | 创建空Bag |
清理 | destroy_empty<...>(bag: Bag) | 销毁空Bag ,若不为空,将报错:ETableNotEmpty |
增 | add<...>(bag: &mut Bag, k: K, v: V) | 添加键值对到Bag 中若 Key 已存在,将报错:EFieldAlreadyExists |
删 | remove<...>(bag: &mut Bag, k: K): V | 从Bag 中删除并返回指定Key 的键值对若 Key 不存在将报错:EFieldDoesNotExist 若值的类型与指定类型不匹配,将报错: EFieldTypeMismatch |
改 | borrow_mut<...>(bag: &Bag, k: K): &mut V | 从Bag 中读取指定Key 的值的可变引用,以便进行对值进行修改若 Key 不存在,将报错:EFieldDoesNotExist 若值的类型与指定类型不匹配,将报错: EFieldTypeMismatch |
查 | borrow<...>(bag: &Bag, k: K): &V | 从Bag 中读取指定Key 的值若 Key 不存在,将报错:EFieldDoesNotExist 若值的类型与指定类型不匹配,将报错: EFieldTypeMismatch |
contains<K: ...>(bag: &Bag, k: K): bool | 若Bag 中包含指定的Key 的值返回true ,否则返回false | |
contains_with_type<K: ..., V: store>(bag: &Bag, k: K): bool | 若Bag 中包含指定的Key 的指定类型的值将返回true ,否则返回false | |
length<...>(bag: &Bag): u64 | 获取Bag 的长度 | |
is_empty<...>(bag: &Bag): bool | 当且仅当Bag 为空时返回true ,否则返回false |
代码示例
示例中定义了一个书架结构(Bookshelf
),其中包含一个Bag
类型的items
,可以放入任意类型的键值对。示例中以在书架上放置书籍(Book
)和玩具(Toy
)两种不同的对象为例。
结构定义
public struct Bookshelf has key {
id: UID,
items: Bag,
}
// 书籍
public struct Book has key, store {
id: UID,
title: String,
description: String,
}
// 玩具
public struct Toy has key, store {
id: UID,
name: String,
category: String,
}
创建书架共享对象
调用
bag::new
方法
// 创建书架
public fun create_bookshelf(ctx: &mut TxContext) {
transfer::share_object(Bookshelf {
id: object::new(ctx),
items: bag::new(ctx),
});
}
放置书本到书架
调用
bag::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)
};
bookshelf.items.add(book.title, book);
}
放置玩具到书架
调用
bag::add
方法
public fun add_toy(bookshelf: &mut Bookshelf, name: vector<u8>, category: vector<u8>, ctx: &mut TxContext) {
let toy = Toy {
id: object::new(ctx),
name: ascii::string(name),
category: ascii::string(category)
};
bookshelf.items.add(toy.name, toy);
}
获取书本
调用
bag::borrow
方法。
public fun get_book(bookshelf: &Bookshelf, title: vector<u8>): &Book {
bookshelf.items.borrow(ascii::string(title))
}
获取玩具
调用
bag::borrow
方法。
public fun get_toy(bookshelf: &Bookshelf, name: vector<u8>): &Toy{
bookshelf.items.borrow(ascii::string(name))
}
设置书本的描述信息
调用
bag::borrow_mut
方法。
public fun set_book_desc(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
let book_mut_ref = bookshelf.items.borrow_mut<_, Book>(ascii::string(title));
book_mut_ref.description = ascii::string(description);
}
判断书本是否存在
调用
bag::contains
方法。
public fun is_book_existed(bookshelf: &Bookshelf, title: vector<u8>): bool {
bookshelf.items.contains(ascii::string(title))
}
从书架上移除书本
调用
bag::remove
方法。
public fun remove_book(bookshelf: &mut Bookshelf, title: vector<u8>): Book {
bookshelf.items.remove(ascii::string(title))
}
从书架上移除玩具
调用
bag::remove
方法。
public fun remove_toy(bookshelf: &mut Bookshelf, name: vector<u8>): Toy{
bookshelf.items.remove(ascii::string(name))
}
判断书架是否为空
调用
bag::is_empty
方法。
public fun is_bookshelf_empty(bookshelf: &Bookshelf): bool {
bookshelf.items.is_empty()
}
获取书架上物品数量
调用
bag::length
方法。
public fun get_count(bookshelf: &Bookshelf): u64{
bookshelf.items.length()
}
销毁空书架
调用
bag::destroy_empty
方法。
public fun destroy_empty_bookshelf(bookshelf: Bookshelf) {
let Bookshelf {id, items} = bookshelf;
items.destroy_empty();
id.delete()
}
完整代码
- bag
module cookbook::bag {
use sui::bag::{Self, Bag};
use std::ascii::{Self, String};
public struct Bookshelf has key {
id: UID,
items: Bag,
}
// 书籍
public struct Book has key, store {
id: UID,
title: String,
description: String,
}
// 玩具
public struct Toy has key, store {
id: UID,
name: String,
category: String,
}
// 创建书架
public fun create_bookshelf(ctx: &mut TxContext) {
transfer::share_object(Bookshelf {
id: object::new(ctx),
items: bag::new(ctx),
});
}
// 销毁空书架
public fun destroy_empty_bookshelf(bookshelf: Bookshelf) {
let Bookshelf {id, items} = bookshelf;
items.destroy_empty();
id.delete()
}
// 放置书籍
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)
};
bookshelf.items.add(book.title, book);
}
// 放置玩具
public fun add_toy(bookshelf: &mut Bookshelf, name: vector<u8>, category: vector<u8>, ctx: &mut TxContext) {
let toy = Toy {
id: object::new(ctx),
name: ascii::string(name),
category: ascii::string(category)
};
bookshelf.items.add(toy.name, toy);
}
// 拿取书本
public fun get_book(bookshelf: &Bookshelf, title: vector<u8>): &Book {
bookshelf.items.borrow(ascii::string(title))
}
// 拿取玩具
public fun get_toy(bookshelf: &Bookshelf, name: vector<u8>): &Toy{
bookshelf.items.borrow(ascii::string(name))
}
// 设置书本描述
public fun set_book_desc(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
let book_mut_ref = bookshelf.items.borrow_mut<_, Book>(ascii::string(title));
book_mut_ref.description = ascii::string(description);
}
// 从书架上移除书本
public fun remove_book(bookshelf: &mut Bookshelf, title: vector<u8>): Book {
bookshelf.items.remove(ascii::string(title))
}
// 从书架上移除玩具
public fun remove_toy(bookshelf: &mut Bookshelf, name: vector<u8>): Toy{
bookshelf.items.remove(ascii::string(name))
}
// 判断书本是否存在
public fun is_book_existed(bookshelf: &Bookshelf, title: vector<u8>): bool {
bookshelf.items.contains(ascii::string(title))
}
// 判断书架是否为空
public fun is_bookshelf_empty(bookshelf: &Bookshelf): bool {
bookshelf.items.is_empty()
}
public fun get_count(bookshelf: &Bookshelf): u64{
bookshelf.items.length()
}
public fun get_book_title(book: &Book): String {
book.title
}
public fun get_book_desc(book: &Book): String {
book.description
}
public fun get_toy_name(toy: &Toy): String {
toy.name
}
public fun get_toy_category(toy: &Toy): String {
toy.category
}
}
- bag_tests
#[test_only]
module cookbook::bag_tests {
use std::ascii;
use sui::test_scenario as ts;
use cookbook::bag::{Bookshelf, create_bookshelf, destroy_empty_bookshelf,
add_book, add_toy, get_book, get_toy, set_book_desc, is_book_existed,
remove_book, is_bookshelf_empty, get_count, get_book_title, get_book_desc,
remove_toy, get_toy_name, get_toy_category};
#[test_only]
use sui::test_utils::assert_eq;
#[test]
public fun test_bag() {
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_count(), 1);
assert_eq(bookshelf.is_bookshelf_empty(), false);
ts::return_shared(bookshelf);
};
// 放置玩具到书架
let expected_name= b"Lego set";
let expected_category= b"Building Blocks";
{
ts.next_tx(alice);
let mut bookshelf: Bookshelf = ts.take_shared();
add_toy(
&mut bookshelf,
expected_name,
expected_category,
ts.ctx(),
);
assert_eq(bookshelf.get_count(), 2);
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));
};
{
let toy = get_toy(
&bookshelf,
expected_name,
);
assert_eq(toy.get_toy_name(), ascii::string(expected_name));
assert_eq(toy.get_toy_category(), ascii::string(expected_category));
};
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);
ts::return_shared(bookshelf);
};
// 从书架上拿走书本和玩具
{
ts.next_tx(alice);
let mut bookshelf: Bookshelf = ts.take_shared();
{
assert_eq(bookshelf.get_count(), 2);
let book = remove_book(
&mut bookshelf,
expected_title,
);
assert_eq(bookshelf.get_count(), 1);
assert_eq(book.get_book_title(), ascii::string(expected_title));
assert_eq(book.get_book_desc(), ascii::string(expected_new_description));
transfer::public_transfer(book, alice);
};
{
assert_eq(bookshelf.get_count(), 1);
let toy = remove_toy(
&mut bookshelf,
expected_name,
);
assert_eq(bookshelf.get_count(), 0);
assert_eq(toy.get_toy_name(), ascii::string(expected_name));
assert_eq(toy.get_toy_category(), ascii::string(expected_category));
transfer::public_transfer(toy, alice);
};
ts::return_shared(bookshelf);
};
// 销毁书架
{
ts.next_tx(alice);
let bookshelf: Bookshelf = ts.take_shared();
destroy_empty_bookshelf(bookshelf);
};
ts.end();
}
}