Skip to main content

near_sdk/store/
mod.rs

1//! Collections and types used when interacting with storage.
2//!
3//! ## Benchmarks of comparison with [`std::collections`]:
4//!
5//! To help you understand how cost-effective near collections are in terms of gas usage compared to native ones,
6//! take a look at this investigation: [Near benchmarking github](https://github.com/volodymyr-matselyukh/near-benchmarking).
7//!
8//! The results of the investigation can be found here: [Results](https://docs.google.com/spreadsheets/d/1ThsBlNR6_Ol9K8cU7BRXNN73PblkTbx0VW_njrF633g/edit?gid=0#gid=0).
9//!
10//! If your collection has up to 100 entries, it's acceptable to use the native collection, as it might be simpler
11//! since you don't have to manage prefixes as we do with near collections.
12//! However, if your collection has 1,000 or more entries, it's better to use a near collection. The investigation
13//! mentioned above shows that running the contains method on a native [`std::collections::HashSet<i32>`] **consumes 41% more gas**
14//! compared to a near [`crate::store::IterableSet<i32>`].
15//!
16//! ## FAQ: most collections of this [`module`](self) persist on `Drop` and `flush`
17//! Unlike containers in [`near_sdk::collections`](crate::collections) module, most containers in current [`module`](self) cache all changes
18//! and loads and only update values that are changed in storage after it’s dropped through it’s [`Drop`] implementation.
19//! Note that [`LookupSet`] is an exception and writes directly to storage on each operation
20//! without using an in-memory cache or a `flush`-based persistence mechanism.
21//!
22//! These changes can be updated in storage before the container variable is dropped by using
23//! the container's `flush` method, e.g. [`IterableMap::flush`](crate::store::IterableMap::flush) ([`IterableMap::drop`](crate::store::IterableMap::drop) uses it in implementation too).
24//!
25//! ```rust,no_run
26//! # use near_sdk::{log, near};
27//! use near_sdk::store::IterableMap;
28//!
29//! #[near(contract_state)]
30//! #[derive(Debug)]
31//! pub struct Contract {
32//!   greeting_map: IterableMap<String, String>,
33//! }
34//!
35//! # impl Default for Contract {
36//! #     fn default() -> Self {
37//! #         let prefix = b"gr_pr";
38//! #         Self {
39//! #             greeting_map: IterableMap::new(prefix.as_slice()),
40//! #         }
41//! #     }
42//! # }
43//!
44//! #[near]
45//! impl Contract {
46//!     pub fn mutating_method(&mut self, argument: String) {
47//!         self.greeting_map.insert("greeting".into(), argument);
48
49//!         near_sdk::env::log_str(&format!("State of contract mutated: {:#?}", self));
50//!     }
51//! }
52//! // expanded #[near] macro call on a contract method definition:
53//! // ...
54//! # let argument = "hello world".to_string();
55//! let mut contract: Contract = ::near_sdk::env::state_read().unwrap_or_default();
56//! // call of the original `mutating_method` as defined in source code prior to expansion
57//! Contract::mutating_method(&mut contract, argument);
58//! ::near_sdk::env::state_write(&contract);
59//! // Drop on `contract` is called! `IterableMap` is only `flush`-ed here  <====
60//! // ...
61//! ```
62//!
63//! ## General description
64//!
65//! These collections are more scalable versions of [`std::collections`] when used as contract
66//! state because it allows values to be lazily loaded and stored based on what is actually
67//! interacted with.
68//!
69//! Fundamentally, a contract's storage is a key/value store where both keys and values are just
70//! [`Vec<u8>`]. If you want to store some structured data, for example, [`Vec<Account>`], one way
71//! to achieve that would be to serialize the Vec to bytes and store that. This has a drawback in
72//! that accessing or modifying a single element would require reading the whole `Vec` from the
73//! storage.
74//!
75//! That's where `store` module helps. Its collections are backed by a key value store.
76//! For example, a store::Vector is stored as several key-value pairs, where indices are the keys.
77//! So, accessing a single element would only load this specific element.
78//!
79//! It's also a bad practice to have a native collection properties as a top level properties of your contract.
80//! The contract will load all the properties before the contract method invocation. That means that all your native
81//! collections will be fully loaded into memory even if they are not used in the method you invoke.
82//!
83//! It can be expensive to load all values into memory, and because of this, `serde`
84//! [`Serialize`](serde::Serialize) and [`Deserialize`](serde::Deserialize) traits are
85//! intentionally not implemented. If you want to return all values from a storage collection from
86//! a function, consider using pagination with the collection iterators.
87//!
88//! All of the collections implement [`BorshSerialize`](borsh::BorshSerialize) and
89//! [`BorshDeserialize`](borsh::BorshDeserialize) to be able to store the metadata of the
90//! collections to be able to access all values. Because only metadata is serialized, these
91//! structures should not be used as a borsh return value from a function.
92//!
93//! ## Calls to **host functions**, used in implementation:
94//!
95//! * [`near_sdk::env::storage_write`](crate::env::storage_write)
96//! * [`near_sdk::env::storage_read`](crate::env::storage_read)
97//! * [`near_sdk::env::storage_remove`](crate::env::storage_remove)
98//! * [`near_sdk::env::storage_has_key`](crate::env::storage_has_key)
99//!
100//! ## Module's glossary:
101//!
102//! The collections are as follows:
103//!
104//! Sequences:
105//!
106//! - [`Vector`]: Analogous to [`Vec`] but not contiguous and persisted to storage.
107//!
108//! Maps:
109//!
110//! - [`LookupMap`]: Wrapper around key-value storage interactions, similar to
111//!   [`UnorderedMap`]/[`std::collections::HashMap`] except that keys are not persisted and cannot be
112//!   iterated over.
113//!
114//! - [`UnorderedMap`]: __DEPRECATED__ storage version of [`std::collections::HashMap`]. No ordering
115//!   guarantees.
116//! - [`IterableMap`]: a replacement with better iteration performance for [`UnorderedMap`], which is being deprecated.
117//!
118//! - [`TreeMap`] (`unstable`): Storage version of [`std::collections::BTreeMap`]. Ordered by key,
119//!   which comes at the cost of more expensive lookups and iteration.
120//!
121//! Sets:
122//!
123//! - [`LookupSet`]: Non-iterable storage version of [`std::collections::HashSet`].
124//!
125//! - [`UnorderedSet`]: __DEPRECATED__ analogous to [`std::collections::HashSet`], and is an iterable
126//!   version of [`LookupSet`] and persisted to storage.
127//! - [`IterableSet`]: a replacement with better iteration performance for [`UnorderedSet`], which is being deprecated.
128//!
129//! Basic Types:
130//!
131//! - [`Lazy<T>`](Lazy): Lazily loaded type that can be used in place of a type `T`.
132//!   Will only be loaded when interacted with and will persist on [`Drop`].
133//!
134//! - [`LazyOption<T>`](LazyOption): Lazily loaded, optional type that can be used in
135//!   place of a type [`Option<T>`](Option). Will only be loaded when interacted with and will
136//!   persist on [`Drop`].
137//!
138//! * More information about collections can be found in [NEAR documentation](https://docs.near.org/smart-contracts/anatomy/collections)
139//! * Benchmarking results of the NEAR-SDK store collections vs native collections can be found in [github](https://github.com/volodymyr-matselyukh/near-benchmarking)
140
141mod lazy;
142pub use lazy::Lazy;
143
144mod lazy_option;
145pub use lazy_option::LazyOption;
146
147pub mod vec;
148pub use vec::Vector;
149
150pub mod lookup_map;
151pub use self::lookup_map::LookupMap;
152
153mod lookup_set;
154pub use self::lookup_set::LookupSet;
155
156pub mod iterable_map;
157pub use self::iterable_map::IterableMap;
158pub mod iterable_set;
159pub use self::iterable_set::IterableSet;
160pub mod unordered_map;
161#[allow(deprecated)]
162pub use self::unordered_map::UnorderedMap;
163
164pub mod unordered_set;
165#[allow(deprecated)]
166pub use self::unordered_set::UnorderedSet;
167
168#[cfg(feature = "unstable")]
169pub mod tree_map;
170#[cfg(feature = "unstable")]
171pub use self::tree_map::TreeMap;
172
173mod index_map;
174pub(crate) use self::index_map::IndexMap;
175
176pub(crate) mod free_list;
177pub(crate) use self::free_list::FreeList;
178
179/// Storage key hash function types and trait to override map hash functions.
180pub mod key;
181
182pub(crate) const ERR_INCONSISTENT_STATE: &str = "The collection is in an inconsistent state. Did previous smart \
183        contract execution terminate unexpectedly?";
184
185pub(crate) const ERR_NOT_EXIST: &str = "Key does not exist in map";