In the previous lesson, we created a struct for our Exam smart contract and implemented the necessary traits to support serialization and deserialization for stable memory on the Internet Computer. In this lesson, we’ll expand on that by setting up thread-local variables and managing stable memory using RefCell and a memory manager.
Previously, we:
This setup enables the contract to persist data across deployments using stable memory.
In Rust, thread-local variables allow the creation of thread-specific storage. Since the Internet Computer is single-threaded, using thread_local! ensures safe and isolated storage that we can still mutate using RefCell.
Rust enforces immutability, but with RefCell, we can mutably borrow data even from a shared, seemingly immutable structure—perfect for our use case in canister smart contracts.
We define a memory_manager inside a thread_local! block. This manager allows us to simulate multiple memory slots within a single stable memory instance by virtually splitting it into 256 slots.
thread_local! {
static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> = RefCell::new(
MemoryManager::init(DefaultMemoryImpl::default())
);
}
ICP allows only one data storage structure per stable memory. To store multiple structures—like Exam data and participation percentages—we must partition the memory. This is where the memory manager comes into play, managing multiple virtual memory segments from a single physical location.
Next, we define the Exam Map to store all exam records using StableBTreeMap. This acts like a dictionary with keys and values:
thread_local! {
static EXAM_MAP: RefCell<StableBTreeMap<u64, Exam, Memory>> = RefCell::new(
StableBTreeMap::init(
MEMORY_MANAGER.with(|m| m.borrow().get_memory_id(0))
)
);
}
We define a second map for participation percentages, using the same memory manager but a different virtual memory ID:
thread_local! {
static PARTICIPATION_PERCENTAGE_MAP: RefCell<StableBTreeMap<u64, u64, Memory>> = RefCell::new(
StableBTreeMap::init(
MEMORY_MANAGER.with(|m| m.borrow().get_memory_id(1))
)
);
}
This map links each exam ID (u64) to a participation percentage value (u64), e.g., 65%.
This setup is reusable and forms the backbone for most ICP-based smart contract projects.
Swap insights and ask questions about “Build on Internet Computer with ICP Rust CDK”.
Ask a question or share your thoughts about this lesson.