Example - My Collections
We are going to demonstrate the use of module collections in pchian-sdk
.
collections
are designed as a data structure for gas efficiency:
- Cacher: allows lazy loading of a field in contract storage
- Vector: lazily stores a list of items
- FastMap: lazily stores items into a key-value map
- IterableMap: lazily stores items into an iterable key-value map
We create the MyCollections
struct which includes all these collection structures.
lib.rs
use pchain_sdk::{
contract, contract_methods, call, Cacher, collections::{Vector, FastMap, IterableMap}
};
type Address = [u8; 32];
#[contract]
pub struct MyCollections {
lazy_cat: Cacher<String>,
pretty_numbers: Vector<i32>,
address_resolver: FastMap<Address, String>,
prices: IterableMap<String, u32>
}
Cacher
Cacher
is a data wrapper to support Lazy Read and Lazy Write to a field in Contract Storage.
#[contract_methods]
impl MyCollections {
/// Here we use the receiver `&self` without loading data before executing this method.
#[call]
fn meow(&self) -> String {
// Actual loading happens here.
// Dereference as immutable and invoke a function as &String
self.lazy_cat.to_uppercase()
}
/// Here we use the receiver `&mut self` without loading data before executing this method.
#[call]
fn feed(&mut self, data: String) {
// Dereference as mutable and then assign a value to it
self.lazy_cat.set(data);
// Actual saving happens after this method
}
}
Vector
/// Here we use the receiver `&self` without loading data before executing this method.
#[call]
fn pick(&self, index: usize) -> Option<i32> {
// Actual loading happens here.
// Dereference as immutable and call functions from iterator
self.pretty_numbers.iter().nth(index).map(|value| *value)
}
/// Here we use the receiver `&mut self` without loading data before executing this method.
#[call]
fn push(&mut self, num: i32) -> usize {
// Dereference as mutable and then set a value to it
self.pretty_numbers.push(&num);
self.pretty_numbers.len()
// Actual saving happens after this method
}
FastMap
/// Here we use the receiver `&self` without loading data before executing this method.
#[call]
fn resolve(&self, address: Address) -> Option<String> {
// Actual loading happens here.
// Dereference as immutable and call functions from key-value map
self.address_resolver.get(&address)
}
/// Here we use the receiver `&mut self` without loading data before executing this method.
#[call]
fn add_record(&mut self, address: Address, name: String) {
// Dereference as mutable and then insert a value
self.address_resolver.insert(&address, name)
// Actual saving happens after this method
}
IterableMap
/// Here we use the receiver `&self` without loading data before executing this method.
#[call]
fn price(&self, item: String) -> Option<u32> {
// Actual loading happens here.
// Dereference as immutable and call functions from key-value map
self.prices.get(&item)
}
/// Here we use the receiver `&mut self` without loading data before executing this method.
#[call]
fn set_price(&mut self, item: String, price: u32) {
// Dereference as mutable and then insert a value
self.prices.insert(&item, price);
// Actual saving happens after this method
}
/// This method the iterable-map to calculate the sum of the values by iterating each item.
#[call]
fn total_price(&self) -> u32 {
self.prices.values().sum()
}