Rise In Logo

Build on Internet Computer with ICP Rust CDK

State Management 2

State Management 2

Managing Memory and State in the Internet Computer Smart Contract

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.

Recap: Struct and Traits

Previously, we:

  • Defined the Exam struct with fields: out_of, course_name, and curve.
  • Applied the following macros:
  • #[derive(CandidType, Serialize, Deserialize)] for Candid compatibility.
  • Implemented two traits:
  • Storable: for converting data to and from bytes.
  • BoundedStorable: to set memory size constraints (max_size = 100, is_fixed_size = false).

This setup enables the contract to persist data across deployments using stable memory.

What Are Thread-Local Variables?

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.

Why Use 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.

Defining the Memory Manager

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())
    );
}

Why We Need a Memory Manager

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.

Creating the Exam Map

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))
        )
    );
}
  • u64 serves as the unique identifier (e.g., 0, 1, 2) for each exam.
  • Exam is the value structure.
  • This setup allows you to store, retrieve, and update exam data reliably across canister upgrades.

Creating the Participation Percentage Map

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%.

Recap of What We’ve Done

  1. Created the Exam struct with serialization traits.
  2. Defined thread_local! memory storage using RefCell to allow internal mutation.
  3. Initialized a memory manager to simulate multiple memory regions within a single stable memory.
  4. Defined two stable maps:
  • EXAM_MAP for exam records.
  • PARTICIPATION_PERCENTAGE_MAP for attendance metrics.

This setup is reusable and forms the backbone for most ICP-based smart contract projects.

Comments

You need to enroll in the course to be able to comment!

Stay in the know

Never miss updates on new programs and opportunities.

Rise In Logo

Rise together in web3!

Disclaimer: The information /programs / events provided on https://patika.dev and https://risein.com are strictly for upskilling and networking purposes related to the technical infrastructure of blockchain platforms. We do not provide financial or investment advice and do not make any representations regarding the value, profitability, or future price of any blockchain or cryptocurrency. Users are encouraged to conduct their own research and consult with licensed financial professionals before engaging in any investment activities. https://patika.dev and https://risein.com disclaim any responsibility for financial decisions made by users based on information provided here.