{"content":{"title":"sui move table_vec、vec_set","body":"## 引言\r\n本文通过阅读分析table_vec和vec_set实现原理，深入了解它们的构成与使用，为阅读deny_list的实现准备条件\r\n\r\ntable_vec是对table的进一层封装  \r\n  \r\n### 结构体  \r\n```rust\r\n    struct TableVec<phantom Element: store> has store {\r\n        /// The contents of the table vector.\r\n        contents: Table<u64, Element>,\r\n    }\r\n```\r\nTableVec规定了储存的table键类型必须是u64，而值是拥有store能力的任意一类类型\r\n\r\n### 创建\r\n```rust\r\n    /// Create an empty TableVec.\r\n    public fun empty<Element: store>(ctx: &mut TxContext): TableVec<Element> {\r\n        TableVec {\r\n            contents: table::new(ctx)\r\n        }\r\n    }\r\n\r\n    /// Return a TableVec of size one containing element `e`.\r\n    public fun singleton<Element: store>(e: Element, ctx: &mut TxContext): TableVec<Element> {\r\n        let t = empty(ctx);\r\n        push_back(&mut t, e);\r\n        t\r\n    }\r\n```\r\n二者的区别是singleton可以在新建TableVec是插入一个值\r\n### 插入/删除\r\n```rust\r\n    public fun push_back<Element: store>(t: &mut TableVec<Element>, e: Element) {\r\n        let key = length(t);\r\n        table::add(&mut t.contents, key, e);\r\n    }\r\n\r\n    public fun pop_back<Element: store>(t: &mut TableVec<Element>): Element {\r\n        let length = length(t);\r\n        assert!(length > 0, EIndexOutOfBound);\r\n        table::remove(&mut t.contents, length - 1)\r\n    }\r\n```\r\n可以发现TableVec通过数组长度获取索引，作为新插入数据的key  \r\npop_back获取了TableVec的长度作为待删除元素的key，key这就意味着在调用删除元素时需要从数组末尾依次删除  \r\n\r\n那么我们需要删除中间的元素怎么办呢？  \r\n合约提供了这两个函数  \r\n```rust\r\n    public fun swap<Element: store>(t: &mut TableVec<Element>, i: u64, j: u64) {\r\n        assert!(length(t) > i, EIndexOutOfBound);\r\n        assert!(length(t) > j, EIndexOutOfBound);\r\n        if (i == j) { return };\r\n        let element_i = table::remove(&mut t.contents, i);\r\n        let element_j = table::remove(&mut t.contents, j);\r\n        table::add(&mut t.contents, j, element_i);\r\n        table::add(&mut t.contents, i, element_j);\r\n    }\r\n\r\n    /// Swap the `i`th element of the TableVec `t` with the last element and then pop the TableVec.\r\n    /// This is O(1), but does not preserve ordering of elements in the TableVec.\r\n    /// Aborts if `i` is out of bounds.\r\n    public fun swap_remove<Element: store>(t: &mut TableVec<Element>, i: u64): Element {\r\n        assert!(length(t) > i, EIndexOutOfBound);\r\n        let last_idx = length(t) - 1;\r\n        swap(t, i, last_idx);\r\n        pop_back(t)\r\n    }\r\n```\r\n它可以让TableVec中的两个元素调换位置  \r\nswap函数首先检查输入索引i，j是否超出范围,如果i，j不相等，那么就会调用table::remove将对应索引的元素取出,然后调用table::add将两个元素对应的索引交换再次插入，这样实现了两个元素的交换。  \r\nswap_remove提供了移除中间值的方法，原理是将待删除的值与最后一个值交换位置，然后再调用pop_back弹出待删除的值  \r\n\r\n### 取值\r\n```rust\r\n     public fun borrow<Element: store>(t: &TableVec<Element>, i: u64): &Element {\r\n        assert!(length(t) > i, EIndexOutOfBound);\r\n        table::borrow(&t.contents, i)\r\n    }\r\n\r\n    /// Add element `e` to the end of the TableVec `t`.\r\n    public fun push_back<Element: store>(t: &mut TableVec<Element>, e: Element) {\r\n        let key = length(t);\r\n        table::add(&mut t.contents, key, e);\r\n    }\r\n```\r\n\r\n### 删除\r\n```rust\r\n\r\n    public fun destroy_empty<Element: store>(t: TableVec<Element>) {\r\n        assert!(length(&t) == 0, ETableNonEmpty);\r\n        let TableVec { contents } = t;\r\n        table::destroy_empty(contents);\r\n    }\r\n\r\n    public fun drop<Element: drop + store>(t: TableVec<Element>) {\r\n        let TableVec { contents } = t;\r\n        table::drop(contents)\r\n    }\r\n```\r\n删除TableVec，区别是drop可以删除非空的TableVec  \r\n\r\n## vec_set\r\nvec_set与TableVec有相似之处  \r\n\r\n### 结构体\r\n```rust\r\n    struct VecSet<K: copy + drop> has copy, drop, store {\r\n        contents: vector<K>,\r\n    }\r\n```\r\nVecSet可以储存任意一类具有copy drop能力的数据集合  \r\n### 创建\r\n```rust\r\n    public fun empty<K: copy + drop>(): VecSet<K> {\r\n        VecSet { contents: vector::empty() }\r\n    }\r\n\r\n    public fun singleton<K: copy + drop>(key: K): VecSet<K> {\r\n        VecSet { contents: vector::singleton(key) }\r\n    }\r\n```\r\n### 添加/删除\r\nVecSet可以保证集合中不会存在重复的元素  \r\n这是因为add在添加元素时会先检查VecSet中是否含有相同的元素\r\n```rust\r\n    public fun contains<K: copy + drop>(self: &VecSet<K>, key: &K): bool {\r\n        option::is_some(&get_idx_opt(self, key))\r\n    }\r\n\r\n    public fun insert<K: copy + drop>(self: &mut VecSet<K>, key: K) {\r\n        assert!(!contains(self, &key), EKeyAlreadyExists);\r\n        vector::push_back(&mut self.contents, key)\r\n    }\r\n```\r\n\r\n在删除中，会从索引0遍历VecSet数组，直到找到相同的元素返回索引，否则返回none，会报错EKeyDoesNotExist，最后根据索引移除相应元素  \r\n```rust\r\n\r\n    fun get_idx_opt<K: copy + drop>(self: &VecSet<K>, key: &K): Option<u64> {\r\n        let i = 0;\r\n        let n = size(self);\r\n        while (i < n) {\r\n            if (vector::borrow(&self.contents, i) == key) {\r\n                return option::some(i)\r\n            };\r\n            i = i + 1;\r\n        };\r\n        option::none()\r\n    }\r\n\r\n    fun get_idx<K: copy + drop>(self: &VecSet<K>, key: &K): u64 {\r\n        let idx_opt = get_idx_opt(self, key);\r\n        assert!(option::is_some(&idx_opt), EKeyDoesNotExist);\r\n        option::destroy_some(idx_opt)\r\n    }\r\n\r\n    public fun remove<K: copy + drop>(self: &mut VecSet<K>, key: &K) {\r\n        let idx = get_idx(self, key);\r\n        vector::remove(&mut self.contents, idx);\r\n    }\r\n```\r\n### 取值\r\n```rust\r\n\r\n    public fun keys<K: copy + drop>(self: &VecSet<K>): &vector<K> {\r\n        &self.contents\r\n    }\r\n```\r\nkeys返回其中集合的借用  \r\n### 删除\r\n```rust\r\n    public fun into_keys<K: copy + drop>(self: VecSet<K>): vector<K> {\r\n        let VecSet { contents } = self;\r\n        contents\r\n    }\r\n```\r\ninto_keys解包VecSet，返回其中储存的集合    \r\n\r\n## 总结\r\ntable_vec和vec_set都是对下层结构table和vector的一种使用方式，如果不满足使用需求，可以按需求自行封装使用table，和vector。其中vec_set参与实现了deny_list。希望能对理解如何使用动态集合的有所帮助。\r\n<!--StartFragment-->\r\n\r\nMove语言学习交流QQ群: 79489587\\\r\nSui官方中文开发者电报群: <https://t.me/sui_dev_cn>\r\n\r\n<!--EndFragment-->"},"author":{"user":"https://learnblockchain.cn/people/18488","address":null},"history":null,"timestamp":1709613097,"version":1}