Incrementing a counter
In this tutorial, you are going to write a program that provides a few basic functions to increment a counter and illustrates the persistence of a value.
For this tutorial, the program declares a COUNTER
as a mutable variable to contain a natural number that represents the current value of the counter.
This program supports the following functions:
-
The
increment
function updates the current value, incrementing by 1 with no return value. -
The
get
function is a simple query that returns the current value of the counter. -
The
set
function updates the current value to the numeric value you specify as an argument.
This tutorial provides a simple example of how you can increment a counter by calling functions on a deployed canister. By calling the function to increment a value multiple times, you can verify that the variable state—that is, the value of the variable between calls—persists.
Like the other sample programs, this tutorial demonstrates a simple, but realistic, workflow in which you perform the following steps:
-
Create a new project.
-
Write a program that compile into a WebAssembly module.
-
Deploy the canister on the local Internet Computer network.
-
Invoke the canister methods to increment then read the value of a counter.
Before you begin
Before starting the tutorial, verify the following:
-
You have downloaded and installed the Rust programming language and Cargo as described in the Rust installation instructions for your operating system.
-
You have downloaded and installed the DFINITY Canister SDK package as described in Download and install.
-
You have downloaded the latest version of the DFINITY Canister Development Kit (CDK) for Rust from the Rust community’s crate registry or from the Rust CDK repository.
-
You have stopped any Internet Computer network processes running on the local computer.
This tutorial takes approximately 20 minutes to complete.
Create a new project
To create a new project directory for this tutorial:
-
Open a terminal shell on your local computer, if you don’t already have one open.
-
Change to the folder you are using for your Internet Computer sample projects.
-
Create a new project by running the following command:
dfx new rust_counter
The command creates a new
rust_counter
project and Git repository for your project. -
Change to your project directory by running the following command:
cd rust_counter
Modify the default configuration
In the Rust Canister Developer Quick Start, you saw that creating a new project adds a default dfx.json
configuration file to your project directory.
Edit canister settings
To modify the default dfx.json
configuration file for a Rust project:
-
Open the
dfx.json
configuration file in a text editor. -
Replace the
canisters.rust_counter
settings with settings for building a canister using thecargo build
command.For example, under the
rust_counter
key, replace themain
andtype
settings with settings like these:"build": "cargo build --target wasm32-unknown-unknown --package rust_counter --release", "candid": "src/rust_counter/counter.did", "wasm": "target/wasm32-unknown-unknown/release/rust_counter.wasm", "type": "custom" }
-
Remove all of the
rust_counter_assets
configuration settings from the file.The sample program for this tutorial doesn’t use any front-end assets, so you can remove those settings from the configuration file.
-
Remove the
defaults
anddfx
version settings because this tutorial doesn’t use these settings.For example, the configuration file looks like this after you modify the settings:
{ "canisters": { "rust_counter": { "build": "cargo build --target wasm32-unknown-unknown --package rust_counter --release", "candid": "src/rust_counter/counter.did", "wasm": "target/wasm32-unknown-unknown/release/rust_counter.wasm", "type": "custom" } }, "networks": { "ic": { "providers": [ "https://gw.dfinity.network" ], "type": "persistent" }, "local": { "bind": "127.0.0.1:8000", "type": "ephemeral" } }, "version": 1 }
-
Save your change and close the
dfx.json
file to continue.
Add Cargo.toml settings to the project
To add Cargo.toml
settings for the project:
-
Check that you are still in the root directory for your project, if needed.
-
Create a new file named
Cargo.toml
if you created the project usingdfx
or open the defaultCargo.toml
file if you created the project using Cargo. -
Use the [workspace] key to specify the source file directories for your program.
For example:
[workspace] members = [ "src/rust_counter", ]
-
Save your changes and close the
Cargo.toml
file to continue.
Modify the default program
The next step is to replace the default source code in the src/rust_counter/main.mo
file with a Rust program that declares the COUNTER
and implements the increment
, get
, and set
functions.
To modify the default template source code:
-
Check that you are still in the root directory for your project, if needed.
-
Rename the default
src/rust_counter/main.mo
file to use a more descriptive name and the Rust file extension by running the following command:mv src/rust_counter/main.mo src/rust_counter/counter.rs
-
Open the
src/rust_counter/counter.rs
file in a text editor and delete the existing content. -
Copy and paste the following sample code into the
counter.rs
file:use ic_cdk_macros::*; use ic_cdk::export::candid; static mut COUNTER: Option<candid::Nat> = None; #[init] fn init() { unsafe { COUNTER = Some(candid::Nat::from(0)); } } #[update] fn increment() -> () { unsafe { COUNTER.as_mut().unwrap().0 += 1u64; } } #[query] fn get() -> candid::Nat { unsafe { COUNTER.as_mut().unwrap().clone() } } #[update] fn set(input: candid::Nat) -> () { unsafe { COUNTER.as_mut().unwrap().0 = input.0; } }
-
Save your changes and close the
counter.rs
file to continue.
Add required files to the source directory
Before you can build the Rust program, you must provide some additional settings in the source code directory.
To complete the environment for building Rust projects to be deployed on the Internet Computer, you need to do the following:
-
Add a second
Cargo.toml
file for the program. -
Add a Candid interface description file to describe the type signatures for the program.
To add the required files:
-
Change to the source code directory for your program.
For example:
cd src/rust_counter
-
Create a second file named
Cargo.toml
and open it in a text editor. -
Configure settings for your project.
For example, you should have a
Cargo.toml
file with settings similar to the following for this tutorial:[package] name = "rust_counter" version = "0.1.0" authors = ["DFINITY <[email protected]>"] edition = "2018" [lib] path = "counter.rs" crate-type = ["cdylib"] [dependencies] ic-cdk = { path = "../../../cdk-rs/src/ic-cdk", version = "0.1.0" } ic-cdk-macros = { path = "../../../cdk-rs/src/ic-cdk-macros", version = "0.1.0" } ic-types = "0.1.1" lazy_static = "1.4.0"
Replace the path to the ic-cdk
andic-cdk-macros
packages with the appropriate path for your local computer. -
Save your changes and close the
Cargo.toml
file to continue. -
Create a new file named
counter.did
. -
Open the file in a text editor, then copy and paste the following
service
definition for theincrement
,get
, andset
functions:service : { "increment": () -> (); "get": () -> (nat) query; "set": (nat) -> (); }
-
Save your changes and close the
counter.did
file to continue.
Start the network and deploy locally
Before you can build the rust_counter
project, you need to connect to the Internet Computer network either running locally in your development environment or running remotely on a sub-network that you can access.
To start the network locally:
-
Open a new terminal window or tab on your local computer and navigate to your project directory.
For example, you can do either of the following if running Terminal on macOS:
-
Click Shell, then select New Tab to open a new terminal in your current working directory.
-
Click Shell and select New Window, then run
cd ~/ic-projects/rust_counter
in the new terminal if yourrust_counter
project is in theic-projects
working folder.
You should now have two terminals open with your project directory as your current working directory.
-
-
Start the Internet Computer network on your local computer by running the following command:
dfx start
After you start the local network, the terminal displays messages about network operations.
-
Leave the terminal that displays network operations open.
-
Switch to your second terminal window or tab.
-
Register, build, and deploy the canister for the project by running the following command:
dfx deploy
Invoke functions on the deployed canister
After successfully deploying the canister, you can test the canister by invoking the functions it provides. For this tutorial:
-
Invoke the
get
function to query the value of the counter. -
Invoke the
increment
function to increment the counter each time it is called. -
Invoke the
set
function to pass an argument to update the counter to an arbitrary value you specify.
To test invoking methods on the deployed canister:
-
Run the following command to invoke the
get
function, which reads the current value of theCOUNTER
variable on the deployed canister:dfx canister call rust_counter get
The command returns the current value of the
COUNTER
variable as zero:(0)
-
Run the following command to invoke the
increment
function to increment the value of theCOUNTER
variable on the deployed canister by one:dfx canister call rust_counter increment
This command increments the value of the variable—changing its state—but does not return the result.
-
Rerun the following command to get the current value of the
COUNTER
variable on the deployed canister:dfx canister call rust_counter get
The command returns the updated value of the
COUNTER
variable as one:(1)
-
Run additional commands to experiment with invoking the functions and using different values.
For example, try commands similar to the following to set and return the counter value:
dfx canister call rust_counter set '(987)' dfx canister call rust_counter get
Returns the current value of 987.
dfx canister call rust_counter increment dfx canister call rust_counter get
Returns the incremented value of 988.
Stop the local network
After you finish experimenting with your program, you can stop the local Internet Computer network so that it doesn’t continue running in the background.
To stop the local network:
-
In the terminal that displays network operations, press Control-C to interrupt the local network process.
-
Stop the Internet Computer network by running the following command:
dfx stop