Dynamic Collections
Sui Framework offers a variety of collection types that build on the dynamic fields and dynamic object fields concepts. These collections are designed to be a safer and more understandable way to store and manage dynamic fields and objects.
For each collection type we will specify the primitive they use, and the specific features they offer.
Unlike dynamic (object) fields which operate on UID, collection types have their own type and allow calling associated functions.
Common Concepts
All of the collection types share the same set of methods, which are:
add
- adds a field to the collectionremove
- removes a field from the collectionborrow
- borrows a field from the collectionborrow_mut
- borrows a mutable reference to a field from the collectioncontains
- checks if a field exists in the collectionlength
- returns the number of fields in the collectionis_empty
- checks if thelength
is 0
All collection types support index syntax for borrow
and borrow_mut
methods. If you see square
brackets in the examples, they are translated into borrow
and borrow_mut
calls.
let hat: &Hat = &bag[b"key"];
let hat_mut: &mut Hat = &mut bag[b"key"];
// is equivalent to
let hat: &Hat = bag.borrow(b"key");
let hat_mut: &mut Hat = bag.borrow_mut(b"key");
In the examples we won't focus on these functions, but rather on the differences between the collection types.
Bag
Bag, as the name suggests, acts as a "bag" of heterogeneous values. It is a simple, non-generic type that can store any data. Bag will never allow orphaned fields, as it tracks the number of fields and can't be destroyed if it's not empty.
// File: sui-framework/sources/bag.move
public struct Bag has key, store {
/// the ID of this bag
id: UID,
/// the number of key-value pairs in the bag
size: u64,
}
Due to Bag storing any types, the extra methods it offers is:
contains_with_type
- checks if a field exists with a specific type
Used as a struct field:
/// Imported from the `sui::bag` module.
use sui::bag::{Self, Bag};
/// An example of a `Bag` as a struct field.
public struct Carrier has key {
id: UID,
bag: Bag
}
Using the Bag:
let mut bag = bag::new(ctx);
// bag has the `length` function to get the number of elements
assert!(bag.length() == 0, 0);
bag.add(b"my_key", b"my_value".to_string());
// length has changed to 1
assert!(bag.length() == 1, 1);
// in order: `borrow`, `borrow_mut` and `remove`
// the value type must be specified
let field_ref: &String = &bag[b"my_key"];
let field_mut: &mut String = &mut bag[b"my_key"];
let field: String = bag.remove(b"my_key");
// length is back to 0 - we can unpack
bag.destroy_empty();
ObjectBag
Defined in the sui::object_bag
module. Identical to Bag, but uses
dynamic object fields internally. Can only store objects as values.
Table
Table is a typed dynamic collection that has a fixed type for keys and values. It is defined in the
sui::table
module.
// File: sui-framework/sources/table.move
public 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,
}
Used as a struct field:
/// Imported from the `sui::table` module.
use sui::table::{Self, Table};
/// Some record type with `store`
public struct Record has store { /* ... */ }
/// An example of a `Table` as a struct field.
public struct UserRegistry has key {
id: UID,
table: Table<address, Record>
}
Using the Table:
#[test] fun test_table() {
let ctx = &mut tx_context::dummy();
// Table requires explicit type parameters for the key and value
// ...but does it only once in initialization.
let mut table = table::new<address, String>(ctx);
// table has the `length` function to get the number of elements
assert!(table.length() == 0, 0);
table.add(@0xa11ce, b"my_value".to_string());
table.add(@0xb0b, b"another_value".to_string());
// length has changed to 2
assert!(table.length() == 2, 2);
// in order: `borrow`, `borrow_mut` and `remove`
let addr_ref = &table[@0xa11ce];
let addr_mut = &mut table[@0xa11ce];
// removing both values
let _addr = table.remove(@0xa11ce);
let _addr = table.remove(@0xb0b);
// length is back to 0 - we can unpack
table.destroy_empty();
ObjectTable
Defined in the sui::object_table
module. Identical to Table, but uses
dynamic object fields internally. Can only store objects as values.
Summary
- Bag - a simple collection that can store any type of data
- ObjectBag - a collection that can store only objects
- Table - a typed dynamic collection that has a fixed type for keys and values
- ObjectTable - same as Table, but can only store objects
LinkedTable
This section is coming soon!