引言本文通过阅读分析table、bag的源码实现,深入理解此类型的使用方法和实现原理。
本文通过阅读分析table、bag的源码实现,深入理解此类型的使用方法和实现原理。
上一篇文章我们阅读了dynamic_field源码,知道了动态字段的实现原理,那么在看table就会很简单了。table.move定义了一个Table结构体
struct Table<phantom K: copy + drop + store, phantom V: store> has key, store {
/// the ID of this table
id: UID,
/// the number of key-value pairs in the table
size: u64,
}
其实这个Table obj就是dynamic_field中为其增添子对象的object,这里来回忆一下
public fun add<Name: copy + drop + store, Value: store>(
// we use &mut UID in several spots for access control
object: &mut UID,
name: Name,
value: Value,
) {
let object_addr = object::uid_to_address(object);
let hash = hash_type_and_key(object_addr, name);
assert!(!has_child_object(object_addr, hash), EFieldAlreadyExists);
let field = Field {
id: object::new_uid_from_hash(hash),
name,
value,
};
add_child_object(object_addr, field)
}
Table就是拥有filed的父对象,其中的size代表了拥有filed动态字段的数量,初始为0.
需要注意的是,Table结构体在创建时就指定好它能储存哪种类型的键值对struct Table<phantom K: copy + drop + store, phantom V: store> has key, store
,不能储存不同类型的键值对。
public fun new<K: copy + drop + store, V: store>(ctx: &mut TxContext): Table<K, V> {
Table {
id: object::new(ctx),
size: 0,
}
}
调用new创建返回一个Table obj,需要指定固定类型的key和value
public fun add<K: copy + drop + store, V: store>(table: &mut Table<K, V>, k: K, v: V) {
field::add(&mut table.id, k, v);
table.size = table.size + 1;
}
public fun remove<K: copy + drop + store, V: store>(table: &mut Table<K, V>, k: K): V {
let v = field::remove(&mut table.id, k);
table.size = table.size - 1;
v
}
要注意的是,添加的key和value必须与table的两个phantom一致
实现很简单,调用dynamic_field的add remove方法,然后变更长度信息
public fun borrow<K: copy + drop + store, V: store>(table: &Table<K, V>, k: K): &V {
field::borrow(&table.id, k)
}
/// Mutably borrows the value associated with the key in the table `table: &mut Table<K, V>`.
/// Aborts with `sui::dynamic_field::EFieldDoesNotExist` if the table does not have an entry with
/// that key `k: K`.
public fun borrow_mut<K: copy + drop + store, V: store>(table: &mut Table<K, V>, k: K): &mut V {
field::borrow_mut(&mut table.id, k)
}
同样是简单调用dynamic_field中的函数取得value的可变和不可变借用
public fun contains<K: copy + drop + store, V: store>(table: &Table<K, V>, k: K): bool {
field::exists_with_type<K, V>(&table.id, k)
}
/// Returns the size of the table, the number of key-value pairs
public fun length<K: copy + drop + store, V: store>(table: &Table<K, V>): u64 {
table.size
}
/// Returns true iff the table is empty (if `length` returns `0`)
public fun is_empty<K: copy + drop + store, V: store>(table: &Table<K, V>): bool {
table.size == 0
}
/// Destroys an empty table
/// Aborts with `ETableNotEmpty` if the table still contains values
public fun destroy_empty<K: copy + drop + store, V: store>(table: Table<K, V>) {
let Table { id, size } = table;
assert!(size == 0, ETableNotEmpty);
object::delete(id)
}
/// Drop a possibly non-empty table.
/// Usable only if the value type `V` has the `drop` ability
public fun drop<K: copy + drop + store, V: drop + store>(table: Table<K, V>) {
let Table { id, size: _ } = table;
object::delete(id)
}
contains判断table相应键是否存在
length返回table动态字段的数目
is_empty判断table是否为空
destroy_empty可以解构一个空的table
drop可以用来解构非空的table
bag与table的差别在于 它可以储存不同类型的键值对 在bag的结构体定义时,bag没有规定键值的类型
struct Bag has key, store {
/// the ID of this bag
id: UID,
/// the number of key-value pairs in the bag
size: u64,
}
其他部分和table的实现一致
table、bag是对dynamic_field的简单封装。其中,table限定了键值对的类型,在创建时需要指定类型,并且只能存储相同类型的键值对;而bag则是一种更灵活的数据结构,可以存储不同类型的键值对。通过本文的阐述,应该可以更深入地理解这两种数据结构的使用方法和实现原理,从而更好地应用于实际的开发中。
Move语言学习交流QQ群: 79489587 Sui官方中文开发者电报群: https://t.me/sui_dev_cn
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!