### Example Canister Dependencies Source: https://github.com/dfinity/stable-structures/blob/main/README.md Dependencies required for the example canister. Ensure these versions are compatible with your project. ```toml [dependencies] ic-cdk = "0.18.3" ic-cdk-macros = "0.18.3" ic-stable-structures = "0.7.2" ``` -------------------------------- ### Complete Canister Example with Stable Structures Source: https://context7.com/dfinity/stable-structures/llms.txt A production-ready example demonstrating the use of stable structures within an Internet Computer canister, including memory management and persistent data storage across upgrades. ```rust use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory}; use ic_stable_structures::{DefaultMemoryImpl, StableBTreeMap}; use std::cell::RefCell; type Memory = VirtualMemory; // Define memory IDs for different data structures const USER_DATA_MEMORY: MemoryId = MemoryId::new(0); const SETTINGS_MEMORY: MemoryId = MemoryId::new(1); thread_local! { // Initialize memory manager in thread-local storage static MEMORY_MANAGER: RefCell> = RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); // Initialize stable structures with dedicated memories static USERS: RefCell> = RefCell::new( StableBTreeMap::init( MEMORY_MANAGER.with(|m| m.borrow().get(USER_DATA_MEMORY)) ) ); static SETTINGS: RefCell> = RefCell::new( StableBTreeMap::init( MEMORY_MANAGER.with(|m| m.borrow().get(SETTINGS_MEMORY)) ) ); } // Query function - retrieves user data #[ic_cdk_macros::query] fn get_user(user_id: u128) -> Option { USERS.with(|users| users.borrow().get(&user_id)) } // Update function - stores user data #[ic_cdk_macros::update] fn set_user(user_id: u128, data: u128) -> Option { USERS.with(|users| users.borrow_mut().insert(user_id, data)) } // Query function - retrieves a setting #[ic_cdk_macros::query] fn get_setting(key: String) -> Option { SETTINGS.with(|settings| settings.borrow().get(&key)) } // Update function - stores a setting #[ic_cdk_macros::update] fn set_setting(key: String, value: String) -> Option { SETTINGS.with(|settings| settings.borrow_mut().insert(key, value)) } // No pre_upgrade or post_upgrade hooks needed! // Data automatically persists across canister upgrades. ``` -------------------------------- ### Initialize and Use BTreeMap in Rust Source: https://context7.com/dfinity/stable-structures/llms.txt Demonstrates initializing a BTreeMap with DefaultMemoryImpl and performing basic operations like insert, get, contains_key, len, is_empty, first_key_value, remove, pop_first, and clear_new. ```rust use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; // Initialize a new BTreeMap with DefaultMemoryImpl let mut map: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); // Insert key-value pairs map.insert(1, "hello".to_string()); map.insert(2, "world".to_string()); map.insert(3, "foo".to_string()); // Retrieve values by key assert_eq!(map.get(&1), Some("hello".to_string())); assert_eq!(map.get(&99), None); // Check if key exists assert!(map.contains_key(&1)); assert!(!map.contains_key(&99)); // Get map statistics assert_eq!(map.len(), 3); assert!(!map.is_empty()); // Get first and last entries assert_eq!(map.first_key_value(), Some((1, "hello".to_string()))); assert_eq!(map.last_key_value(), Some((3, "foo".to_string()))); // Remove entries let removed = map.remove(&2); assert_eq!(removed, Some("world".to_string())); // Pop first/last entries let first = map.pop_first(); assert_eq!(first, Some((1, "hello".to_string()))); // Clear all entries map.clear_new(); assert!(map.is_empty()); ``` -------------------------------- ### Running Fuzzers Locally Source: https://github.com/dfinity/stable-structures/blob/main/README.md Instructions for setting up and running fuzz tests for stable structures locally. This requires installing the nightly Rust toolchain and the `cargo-fuzz` tool. ```sh rustup toolchain install nightly cargo install cargo-fuzz # To list available fuzzer targets cargo +nightly fuzz list # To run a target cargo +nightly fuzz run ``` -------------------------------- ### StableBTreeMap Implementation in Rust Source: https://github.com/dfinity/stable-structures/blob/main/README.md This Rust code demonstrates how to initialize and use a StableBTreeMap within an Internet Computer canister. It includes functions for getting and inserting key-value pairs. ```rust use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory}; use ic_stable_structures::{DefaultMemoryImpl, StableBTreeMap}; use std::cell::RefCell; type Memory = VirtualMemory; thread_local! { // The memory manager is used for simulating multiple memories. Given a `MemoryId` it can // return a memory that can be used by stable structures. static MEMORY_MANAGER: RefCell> = RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); // Initialize a `StableBTreeMap` with `MemoryId(0)`. static MAP: RefCell> = RefCell::new( StableBTreeMap::init( MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))) ) ); } // Retrieves the value associated with the given key if it exists. #[ic_cdk_macros::query] fn get(key: u128) -> Option { MAP.with(|p| p.borrow().get(&key)) } // Inserts an entry into the map and returns the previous value of the key if it exists. #[ic_cdk_macros::update] fn insert(key: u128, value: u128) -> Option { MAP.with(|p| p.borrow_mut().insert(key, value)) } ``` -------------------------------- ### Stable Vec Operations in Rust Source: https://context7.com/dfinity/stable-structures/llms.txt Illustrates push, pop, get, set, length, emptiness check, iteration, and clearing operations for a stable vector. Requires MemoryManager initialization. ```rust use ic_stable_structures::{Vec as StableVec, DefaultMemoryImpl}; use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); let vec: StableVec = StableVec::init(mem_mgr.get(MemoryId::new(0))); // Push elements vec.push(&10); vec.push(&20); vec.push(&30); // Get length and check if empty assert_eq!(vec.len(), 3); assert!(!vec.is_empty()); // Access elements by index assert_eq!(vec.get(0), Some(10)); assert_eq!(vec.get(1), Some(20)); assert_eq!(vec.get(2), Some(30)); assert_eq!(vec.get(99), None); // Modify elements vec.set(1, &25); assert_eq!(vec.get(1), Some(25)); // Pop last element assert_eq!(vec.pop(), Some(30)); assert_eq!(vec.len(), 2); // Iterate over elements for item in vec.iter() { println!("Item: {}", item); } // Clear all elements vec.clear(); assert!(vec.is_empty()); ``` -------------------------------- ### Stable Cell for Single Value Storage in Rust Source: https://context7.com/dfinity/stable-structures/llms.txt Shows how to initialize, get, and set a single serializable value using Stable Cell. Requires implementing the Storable trait for the data type. ```rust use ic_stable_structures::{Cell, DefaultMemoryImpl, Storable, storable::Bound}; use std::borrow::Cow; use std::cell::RefCell; #[derive(Clone)] struct Config { name: String, version: u32, enabled: bool, } impl Storable for Config { fn to_bytes(&self) -> Cow<'_, [u8]> { let mut bytes = Vec::new(); bytes.extend_from_slice(&self.version.to_le_bytes()); bytes.push(if self.enabled { 1 } else { 0 }); bytes.extend_from_slice(self.name.as_bytes()); Cow::Owned(bytes) } fn into_bytes(self) -> Vec { self.to_bytes().into_owned() } fn from_bytes(bytes: Cow<[u8]>) -> Self { let version = u32::from_le_bytes(bytes[0..4].try_into().unwrap()); let enabled = bytes[4] == 1; let name = String::from_utf8(bytes[5..].to_vec()).unwrap(); Config { name, version, enabled } } const BOUND: Bound = Bound::Unbounded; } // Initialize a cell with a default value let cell: Cell = Cell::init( DefaultMemoryImpl::default(), Config { name: "MyApp".to_string(), version: 1, enabled: true, } ); // Read the current value let config = cell.get(); assert_eq!(config.version, 1); // Update the value (set returns the old value) let mut new_config = cell.get().clone(); new_config.version = 2; let old_config = cell.set(new_config); assert_eq!(old_config.version, 1); assert_eq!(cell.get().version, 2); ``` -------------------------------- ### Initialize and Use MinHeap - Priority Queue Source: https://context7.com/dfinity/stable-structures/llms.txt Shows how to initialize a MinHeap, push elements, check its size, peek at the smallest element, and pop elements in ascending order. Iteration order is not guaranteed to be sorted. ```rust use ic_stable_structures::{MinHeap, DefaultMemoryImpl}; use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); let mut heap: MinHeap = MinHeap::init(mem_mgr.get(MemoryId::new(0))); // Push elements (in any order) heap.push(&30); heap.push(&10); heap.push(&20); heap.push(&5); heap.push(&15); // Length and empty check assert_eq!(heap.len(), 5); assert!(!heap.is_empty()); // Peek at the smallest element without removing it assert_eq!(heap.peek(), Some(5)); // Pop elements (always returns the smallest) assert_eq!(heap.pop(), Some(5)); assert_eq!(heap.pop(), Some(10)); assert_eq!(heap.pop(), Some(15)); assert_eq!(heap.pop(), Some(20)); assert_eq!(heap.pop(), Some(30)); assert_eq!(heap.pop(), None); // Iterate over elements (arbitrary order, not sorted) heap.push(&3); heap.push(&1); heap.push(&2); for item in heap.iter() { println!("Heap item: {}", item); } // Clear the heap heap.clear(); assert!(heap.is_empty()); ``` -------------------------------- ### Initialize and Use BTreeMap Source: https://github.com/dfinity/stable-structures/blob/main/README.md Demonstrates the basic initialization and usage of a BTreeMap with DefaultMemoryImpl. Ensure the memory implementation is suitable for your environment (stable memory in canisters, VectorMemory otherwise). ```rust use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; let mut map: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); map.insert(1, 2); assert_eq!(map.get(&1), Some(2)); ``` -------------------------------- ### Initialize and Use MemoryManager for Virtual Memory Source: https://context7.com/dfinity/stable-structures/llms.txt Demonstrates how to initialize a MemoryManager and create independent virtual memories for different stable structures. Emphasizes the importance of unique MemoryIds to prevent data corruption. ```rust use ic_stable_structures::{ BTreeMap, BTreeSet, Vec as StableVec, DefaultMemoryImpl, memory_manager::{MemoryId, MemoryManager, VirtualMemory}, }; type Memory = VirtualMemory; // Initialize the memory manager let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); // Create virtual memories for different data structures // Each structure MUST have its own unique MemoryId let map: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(0))); let set: BTreeSet = BTreeSet::init(mem_mgr.get(MemoryId::new(1))); let vec: StableVec = StableVec::init(mem_mgr.get(MemoryId::new(2))); // Use structures independently - they don't interfere with each other map.insert(1, "one".to_string()); set.insert(100); vec.push(&42); assert_eq!(map.get(&1), Some("one".to_string())); assert!(set.contains(&100)); assert_eq!(vec.get(0), Some(42)); // Memory IDs 0-254 are available (255 is reserved) // Reusing the same ID for different structures causes data corruption! // WRONG: let another_map: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(0))); ``` -------------------------------- ### Initialize and Use StableBTreeMap with Primitives Source: https://github.com/dfinity/stable-structures/blob/main/examples/README.md Demonstrates initializing a StableBTreeMap for primitive types and inserting/retrieving data. Data persists across canister upgrades. ```bash dfx start --background dfx deploy dfx canister call basic_example insert '(1:nat, 2:nat)' dfx canister call basic_example insert '(3:nat, 4:nat)' dfx deploy --upgrade-unchanged basic_example dfx canister call basic_example get '(1:nat)' > (opt (2 : nat)) dfx canister call basic_example get '(3:nat)' > (opt (4 : nat)) ``` -------------------------------- ### Initialize and Use BTreeSet in Rust Source: https://context7.com/dfinity/stable-structures/llms.txt Demonstrates initializing a BTreeSet using a MemoryManager and performing operations like insert, contains, first, last, remove, pop_first, pop_last, and clear. ```rust use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); let mut set: BTreeSet = BTreeSet::new(mem_mgr.get(MemoryId::new(0))); // Insert elements (returns true if element was new) assert!(set.insert(42)); assert!(set.insert(7)); assert!(set.insert(15)); assert!(!set.insert(42)); // Already exists // Check membership assert!(set.contains(&42)); assert!(!set.contains(&100)); // Get first and last elements assert_eq!(set.first(), Some(7)); assert_eq!(set.last(), Some(42)); // Remove elements assert!(set.remove(&15)); assert!(!set.remove(&999)); // Doesn't exist // Pop first/last assert_eq!(set.pop_first(), Some(7)); assert_eq!(set.pop_last(), Some(42)); // Clear the set set.clear(); assert!(set.is_empty()); ``` -------------------------------- ### Initialize and Use StableBTreeMap with Strings and Vectors Source: https://github.com/dfinity/stable-structures/blob/main/examples/README.md Showcases initializing a StableBTreeMap for strings and vectors (blobs) and performing insert/get operations. Data remains available after canister upgrades. ```bash dfx start --background dfx deploy dfx canister call vecs_and_strings insert '("alice", blob "12341234")' dfx canister call vecs_and_strings insert '("bob", blob "789789789")' dfx deploy --upgrade-unchanged vecs_and_strings dfx canister call vecs_and_strings get '("alice")' > (opt blob "12341234") dfx canister call vecs_and_strings get '("bob")' > (opt blob "789789789") ``` -------------------------------- ### Initialize and Use BTreeSet Source: https://github.com/dfinity/stable-structures/blob/main/README.md Shows how to initialize and use a BTreeSet with DefaultMemoryImpl. This set stores unique elements and supports efficient insertion, deletion, and lookup. ```rust use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; let mut set: BTreeSet = BTreeSet::new(DefaultMemoryImpl::default()); set.insert(42); assert!(set.contains(&42)); assert_eq!(set.pop_first(), Some(42)); assert!(set.is_empty()); ``` -------------------------------- ### Initialize and Use Log - Append-Only List Source: https://context7.com/dfinity/stable-structures/llms.txt Demonstrates initializing an append-only Log structure, appending entries, retrieving them by index, and checking its properties. Requires two memories for index and data. ```rust use ic_stable_structures::{Log, DefaultMemoryImpl}; use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); // Log requires two memories: one for the index, one for the data let log: Log = Log::init( mem_mgr.get(MemoryId::new(0)), // Index memory mem_mgr.get(MemoryId::new(1)), // Data memory ); // Append entries (returns the index of the appended entry) let idx0 = log.append(&"First entry".to_string()).unwrap(); let idx1 = log.append(&"Second entry".to_string()).unwrap(); let idx2 = log.append(&"Third entry".to_string()).unwrap(); assert_eq!(idx0, 0); assert_eq!(idx1, 1); assert_eq!(idx2, 2); // Get entry by index assert_eq!(log.get(0), Some("First entry".to_string())); assert_eq!(log.get(1), Some("Second entry".to_string())); assert_eq!(log.get(99), None); // Get first and last entries assert_eq!(log.first(), Some("First entry".to_string())); assert_eq!(log.last(), Some("Third entry".to_string())); // Get length assert_eq!(log.len(), 3); assert!(!log.is_empty()); // Iterate over entries for entry in log.iter() { println!("Log entry: {}", entry); } // Get memory usage statistics println!("Index size: {} bytes", log.index_size_bytes()); println!("Data size: {} bytes", log.data_size_bytes()); // Clear all entries log.clear(); assert!(log.is_empty()); ``` -------------------------------- ### Initialize and Use MemoryManager with BTreeMap Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/concepts/memory-manager.md Demonstrates initializing a MemoryManager and creating two independent BTreeMaps using separate virtual memories. Each map operates independently. ```rust use ic_stable_structures::{ memory_manager::{MemoryId, MemoryManager}, BTreeMap, DefaultMemoryImpl, }; // Initialize a MemoryManager with DefaultMemoryImpl as the underlying memory let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); // Create two separate BTreeMaps, each with its own virtual memory let mut map_1: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(0))); let mut map_2: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(1))); // Demonstrate independent operation of the two maps map_1.insert(1, 2); map_2.insert(1, 3); assert_eq!(map_1.get(&1), Some(2)); // Succeeds as expected ``` -------------------------------- ### Initialize and Use StableBTreeMap with Custom Structs Source: https://github.com/dfinity/stable-structures/blob/main/examples/README.md Illustrates storing custom structs (e.g., UserProfile) in a StableBTreeMap. Data is preserved even after canister upgrades. ```bash dfx canister call custom_types_example insert '(1, record { age = 32; name = "Some Name"})' dfx canister call custom_types_example insert '(2, record { age = 48; name = "Other Name"})' dfx deploy --upgrade-unchanged custom_types_example $ dfx canister call custom_types_example get '(1)' > (opt record { age = 32 : nat8; name = "Some Name" }) dfx canister call custom_types_example get '(2)' > (opt record { age = 48 : nat8; name = "Other Name" }) ``` -------------------------------- ### Initialize Stable BTreeMap with DefaultMemoryImpl Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/concepts/memory-trait.md Initializes a stable `BTreeMap` using `DefaultMemoryImpl`. This is the recommended approach for most use cases, as `DefaultMemoryImpl` automatically selects the appropriate memory backend based on the environment. ```rust use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; let mut map: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); ``` -------------------------------- ### Insert and Retrieve Stable Data Source: https://github.com/dfinity/stable-structures/blob/main/examples/src/quick_start/README.md Use dfx commands to insert data into a canister's stable storage, upgrade the canister, and then retrieve the data to verify persistence. ```bash dfx start --background --clean # Insert some data into the quick_start canister. dfx canister call quick_start stable_insert '(1:nat, 2:nat)' dfx canister call quick_start stable_insert '(3:nat, 4:nat)' dfx canister call quick_start set_heap_data '(vec {1:nat8; 2:nat8; 3:nat8})' # Upgrade the canister, which clears all the data in the heap. dfx deploy --upgrade-unchanged quick_start # Even though the canister has been upgraded and its heap is cleared, # querying the canister should still return the data stored prior to # the upgrade. dfx canister call quick_start stable_get '(1:nat)' > (opt (2 : nat)) dfx canister call quick_start stable_get '(3:nat)' > (opt (4 : nat)) dfx canister call quick_start get_heap_data > (blob "\01\02\03") ``` -------------------------------- ### Dependencies Configuration for Stable Structures Source: https://context7.com/dfinity/stable-structures/llms.txt Configure your Cargo.toml file to include the necessary dependencies for using stable structures in your Internet Computer canister project. ```toml [dependencies] ic-cdk = "0.18.3" ic-cdk-macros = "0.18.3" ic-stable-structures = "0.7.2" # Optional: for custom type serialization candid = "0.10.14" serde = { version = "1.0", features = ["derive"] } ``` -------------------------------- ### Iterate and Query Ranges in BTreeMap (Rust) Source: https://context7.com/dfinity/stable-structures/llms.txt Shows how to iterate over BTreeMap entries, keys, and values, and perform range queries using inclusive and exclusive bounds. ```rust use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; use std::ops::Bound; let mut map: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); // Insert sample data for i in 1..=10 { map.insert(i, format!("value_{}", i)); } // Iterate over all entries (sorted by key) for entry in map.iter() { println!("Key: {}, Value: {}", entry.key(), entry.value()); } // Iterate over keys only for key in map.keys() { println!("Key: {}", key); } // Iterate over values only for value in map.values() { println!("Value: {}", value); } // Range query: keys from 3 to 7 (inclusive) for entry in map.range((Bound::Included(3), Bound::Included(7))) { println!("Range entry: {} -> {}", entry.key(), entry.value()); } // Range query: keys >= 5 for entry in map.range(5..) { println!("From 5: {} -> {}", entry.key(), entry.value()); } // Range query: keys < 4 for entry in map.range(..4) { println!("Below 4: {} -> {}", entry.key(), entry.value()); } // Start iteration from key before a given bound for entry in map.iter_from_prev_key(&5) { println!("From prev of 5: {} -> {}", entry.key(), entry.value()); } ``` -------------------------------- ### BTreeSet Set Operations in Rust Source: https://context7.com/dfinity/stable-structures/llms.txt Demonstrates union, intersection, symmetric difference, range iteration, subset/superset checks, and disjoint checks for BTreeSet. Ensure MemoryManager is initialized and memory regions are allocated. ```rust use ic_stable_structures::{BTreeSet, DefaultMemoryImpl}; use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); let mut set1: BTreeSet = BTreeSet::new(mem_mgr.get(MemoryId::new(0))); let mut set2: BTreeSet = BTreeSet::new(mem_mgr.get(MemoryId::new(1))); // Populate sets for i in 1..=5 { set1.insert(i); } for i in 3..=7 { set2.insert(i); } // set1 = {1, 2, 3, 4, 5} // set2 = {3, 4, 5, 6, 7} // Union: elements in either set let union: Vec<_> = set1.union(&set2).collect(); assert_eq!(union, vec![1, 2, 3, 4, 5, 6, 7]); // Intersection: elements in both sets let intersection: Vec<_> = set1.intersection(&set2).collect(); assert_eq!(intersection, vec![3, 4, 5]); // Symmetric difference: elements in one but not both let sym_diff: Vec<_> = set1.symmetric_difference(&set2).collect(); assert_eq!(sym_diff, vec![1, 2, 6, 7]); // Range iteration let range: Vec<_> = set1.range(2..=4).collect(); assert_eq!(range, vec![2, 3, 4]); // Subset/superset checks let mut subset: BTreeSet = BTreeSet::new(mem_mgr.get(MemoryId::new(2))); subset.insert(3); subset.insert(4); assert!(subset.is_subset(&set1)); assert!(set1.is_superset(&subset)); // Disjoint check let mut disjoint: BTreeSet = BTreeSet::new(mem_mgr.get(MemoryId::new(3))); disjoint.insert(100); assert!(set1.is_disjoint(&disjoint)); ``` -------------------------------- ### Manage Multiple BTreeMaps with MemoryManager Source: https://github.com/dfinity/stable-structures/blob/main/README.md Illustrates how to use MemoryManager to create distinct virtual memories for multiple stable structures, preventing memory conflicts. Each BTreeMap is assigned a unique MemoryId. ```rust use ic_stable_structures::{ memory_manager::{MemoryId, MemoryManager}, BTreeMap, DefaultMemoryImpl, }; let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); let mut map_1: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(0))); let mut map_2: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(1))); map_1.insert(1, 2); map_2.insert(1, 3); assert_eq!(map_1.get(&1), Some(2)); // Succeeds, as expected. ``` -------------------------------- ### Create a New Git Branch Source: https://github.com/dfinity/stable-structures/blob/main/Contributing.md Use this command to create a new branch for your fix or feature. Replace 'my-branch-name-here' with a descriptive name for your branch. ```bash git checkout -b my-branch-name-here ``` -------------------------------- ### Problematic Multiple Stable Structure Initialization Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/concepts/memory-trait.md Demonstrates an issue where multiple stable structures initialized with the same `DefaultMemoryImpl` instance can interfere with each other, leading to incorrect behavior. Each stable structure requires its own dedicated memory. ```rust use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; let mut map_1: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); let mut map_2: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); map_1.insert(1, 2); map_2.insert(1, 3); assert_eq!(map_1.get(&1), Some(2)); // This assertion fails. ``` -------------------------------- ### Stage Changed Files with Git Source: https://github.com/dfinity/stable-structures/blob/main/Contributing.md Add the contents of changed files to the Git index to prepare them for commit. Replace 'path-to-changed-file' with the actual path to the file you modified. ```bash git add path-to-changed-file ``` -------------------------------- ### Commit Changes with Git Source: https://github.com/dfinity/stable-structures/blob/main/Contributing.md Commit your staged changes to the local repository with a descriptive message. The message should clearly explain the fix or improvement being committed. ```bash git commit -m "Description of the fix being committed." ``` -------------------------------- ### Push Changes to Remote Repository Source: https://github.com/dfinity/stable-structures/blob/main/Contributing.md Push your committed changes from your local repository to the remote repository on GitHub. Ensure 'my-branch-name-here' matches the name of the branch you created. ```bash git push origin my-branch-name-here ``` -------------------------------- ### Add 'created_at' Attribute to Asset Struct Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/schema-upgrades.md Demonstrates adding a new `created_at` field (u64) to the `Asset` struct. The `#[serde(default)]` attribute ensures that if the field is missing during deserialization, it will be initialized with the default value for u64 (which is 0). ```rust #[derive(Serialize, Deserialize)] struct Asset { // The contents of the asset. contents: Vec, // The timestamp the asset was created at. #[serde(default)] created_at: u64, } ``` -------------------------------- ### Update Package Version in Cargo.toml Source: https://github.com/dfinity/stable-structures/blob/main/RELEASE_GUIDE.md Modify the version field in the Cargo.toml file to reflect the new release version. ```toml [package] version = "x.y.z" ``` -------------------------------- ### Add Optional 'uploaded_by' Attribute to Asset Struct Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/schema-upgrades.md Shows how to add an optional `uploaded_by` field (Option) to the `Asset` struct. This is useful when a new attribute does not have a sensible default value. ```rust #[derive(Serialize, Deserialize, CandidType)] struct Asset { // The contents of the asset. contents: Vec, // The timestamp the asset was created at. #[serde(default)] created_at: u64, // The username of the uploader. uploaded_by: Option, } ``` -------------------------------- ### Implement Storable Trait for Custom Type Serialization Source: https://context7.com/dfinity/stable-structures/llms.txt Implement the Storable trait for custom Rust types to enable serialization and deserialization for use with stable structures. Define serialization methods and specify size bounds for memory optimizations. ```rust use ic_stable_structures::{BTreeMap, DefaultMemoryImpl, Storable, storable::Bound}; use std::borrow::Cow; // A custom user profile type #[derive(Clone, Debug, PartialEq)] struct UserProfile { id: u64, name: String, age: u8, } impl Storable for UserProfile { // Serialize the struct to bytes fn to_bytes(&self) -> Cow<'_, [u8]> { let mut bytes = Vec::new(); bytes.extend_from_slice(&self.id.to_le_bytes()); bytes.push(self.age); bytes.extend_from_slice(self.name.as_bytes()); Cow::Owned(bytes) } // Optimized serialization that consumes self fn into_bytes(self) -> Vec { let mut bytes = Vec::with_capacity(9 + self.name.len()); bytes.extend_from_slice(&self.id.to_le_bytes()); bytes.push(self.age); bytes.extend_from_slice(self.name.as_bytes()); bytes } // Deserialize bytes back to struct fn from_bytes(bytes: Cow<[u8]>) -> Self { let id = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); let age = bytes[8]; let name = String::from_utf8(bytes[9..].to_vec()).unwrap(); UserProfile { id, name, age } } // Specify size bounds: // - Unbounded: no size limit, recommended for variable-size types // - Bounded: max_size known, enables memory optimizations const BOUND: Bound = Bound::Bounded { max_size: 8 + 1 + 100, // id + age + max name length is_fixed_size: false, // Size varies due to name }; } // Use the custom type in a BTreeMap let mut map: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); let user = UserProfile { id: 1, name: "Alice".to_string(), age: 30, }; map.insert(user.id, user.clone()); assert_eq!(map.get(&1), Some(user)); ``` -------------------------------- ### Define Asset Struct with Storable Implementation Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/schema-upgrades.md This Rust code defines an `Asset` struct and implements the `Storable` trait for it, enabling it to be stored in stable memory using CBOR encoding. ```rust #[derive(Serialize, Deserialize, CandidType)] struct Asset { // The contents of the asset. contents: Vec, } impl Storable for Asset { fn to_bytes(&self) -> std::borrow::Cow<'_, [u8]> { let mut bytes = vec![]; ciborium::ser::into_writer(&self, &mut bytes).unwrap(); Cow::Owned(bytes) } fn into_bytes(self) -> Vec { let mut bytes = vec![]; ciborium::ser::into_writer(&self, &mut bytes).unwrap() } fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { ciborium::de::from_reader(&*bytes).expect("deserialization must succeed.") } const BOUND: Bound = Bound::Unbounded; } ``` -------------------------------- ### Define the Memory Trait Source: https://github.com/dfinity/stable-structures/blob/main/docs/src/concepts/memory-trait.md The `Memory` trait defines the interface for managing memory, mirroring WebAssembly memory operations. Implementations must handle memory size, growth, reading, and writing. ```rust pub trait Memory { /// Equivalent to WebAssembly memory.size. fn size(&self) -> u64; /// Equivalent to WebAssembly memory.grow. /// Returns the previous size, or -1 if the grow fails. fn grow(&self, pages: u64) -> i64; /// Copies bytes from this memory to the heap (in Wasm, memory 0). /// Panics or traps if out of bounds. fn read(&self, offset: u64, dst: &mut [u8]); /// Writes bytes from the heap (in Wasm, memory 0) to this memory. /// Panics or traps if out of bounds. fn write(&self, offset: u64, src: &[u8]); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.