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