Collections
Collection types are a fundamental part of any programming language. They are used to store a
collection of data, such as a list of items. The vector
type has already been covered in the
vector section, and in this chapter we will cover the vector-based
collection types offered by the Sui Framework.
Vector
While we have previously covered the vector
type in the
vector section, it is worth going over it again in a new context. This
time we will cover the usage of the vector
type in objects and how it can be used in an
application.
module book::collections_vector {
use std::string::String;
/// The Book that can be sold by a `BookStore`
public struct Book has key, store {
id: UID,
name: String
}
/// The BookStore that sells `Book`s
public struct BookStore has key, store {
id: UID,
books: vector<Book>
}
}
VecSet
VecSet
is a collection type that stores a set of unique items. It is similar to a vector
, but it
does not allow duplicate items. This makes it useful for storing a collection of unique items, such
as a list of unique IDs or addresses.
module book::collections_vec_set {
use sui::vec_set::{Self, VecSet};
public struct App has drop {
/// `VecSet` used in the struct definition
subscribers: VecSet<address>
}
#[test]
fun vec_set_playground() {
let set = vec_set::empty<u8>(); // create an empty set
let mut set = vec_set::singleton(1); // create a set with a single item
set.insert(2); // add an item to the set
set.insert(3);
assert!(set.contains(&1), 0); // check if an item is in the set
assert!(set.size() == 3, 1); // get the number of items in the set
assert!(!set.is_empty(), 2); // check if the set is empty
set.remove(&2); // remove an item from the set
}
}
VecSet will fail on attempt to insert an item that already exists in the set.
VecMap
VecMap
is a collection type that stores a map of key-value pairs. It is similar to a VecSet
, but
it allows you to associate a value with each item in the set. This makes it useful for storing a
collection of key-value pairs, such as a list of addresses and their balances, or a list of user IDs
and their associated data.
Keys in a VecMap
are unique, and each key can only be associated with a single value. If you try
to insert a key-value pair with a key that already exists in the map, the old value will be replaced
with the new value.
module book::collections {
use std::string::String;
use sui::vec_map::{Self, VecMap};
public struct Metadata has drop {
name: String,
/// `VecMap` used in the struct definition
attributes: VecMap<String, String>
}
#[test]
fun vec_map_playground() {
let mut map = vec_map::empty(); // create an empty map
map.insert(2, b"two".to_string()); // add a key-value pair to the map
map.insert(3, b"three".to_string());
assert!(map.contains(&2), 0); // check if a key is in the map
map.remove(&2); // remove a key-value pair from the map
}
}
Limitations
Standard collection types are a great way to store typed data with guaranteed safety and consistency. However, they are limited by the type of data they can store - the type system won't allow you to store a wrong type in a collection; and they're limited in size - by the object size limit. They will work for relatively small-sized sets and lists, but for larger collections you may need to use a different approach.
Another limitations on collection types is inability to compare them. Because the order of insertion
is not guaranteed, an attempt to compare a VecSet
to another VecSet
may not yield the expected
results.
This behavior is caught by the linter and will emit a warning: Comparing collections of type 'sui::vec_set::VecSet' may yield unexpected result
let mut set1 = vec_set::empty();
set1.insert(1);
set1.insert(2);
let mut set2 = vec_set::empty();
set2.insert(2);
set2.insert(1);
assert!(set1 == set2, 0);
In the example above, the comparison will fail because the order of insertion is not guaranteed, and
the two VecSet
instances may have different orders of elements. And the comparison will fail even
if the two VecSet
instances contain the same elements.
Summary
- Vector is a native type that allows storing a list of items.
- VecSet is built on top of vector and allows storing sets of unique items.
- VecMap is used to store key-value pairs in a map-like structure.
- Vector-based collections are strictly typed and limited by the object size limit and are best suited for small-sized sets and lists.
Next Steps
In the next section we will cover Dynamic Fields - an important primitive that allows for Dynamic Collections - a way to store large collections of data in a more flexible, yet more expensive way.