Rust canister init function not called

I have a simple canister with the code like this

static mut TOKEN: Option<VotingToken> = None;

#[init]
fn init() {
    ic_cdk::print("!!! Calling init");

    unsafe {
        TOKEN = Some(VotingToken {
            name: "Default token".into(),
            balances: HashMap::new(),
        });
    }
}

But it seems like init() function is not called automatically after canister deployment. What am I doing wrong?

Another question: on the docs site there are several rust canister tutorials, two of which propose different storage management approaches (Adding and searching simple records :: Internet Computer / Incrementing a counter :: Internet Computer). What’s the difference?

2 Likes

Nevermind, was using the old version of DFX.

But the second question still matters.

Hm… it looks like it doesn’t get called on upgrade also.
After a canister upgrade there is no TOKEN anymore - it is uninitialized.

dfx 0.7.0

Yes, there are too few examples about rust canister.

about your first question, I run in 0.7.0 dfx and it inits succeed:

#[init]
fn init() {
    unsafe {
        COUNTER = Some(candid::Nat::from(0));
    }
}
sudo dfx canister call hello_rs get
(0)

the result is (0), not (null).

The second question I am also very confused, when do I need to choose different storage? What are their advantages and disadvantages.

The problem I have is that if I want to enter a parameter during initialization, I don’t know how to do it:

#[init]
fn init(a : candid::Nat) {
    unsafe {
        COUNTER = Some(candid::Nat::from(a));
    }
}

the results:

sudo dfx canister install hello_rs --argument='(1)' -m=reinstall
Reinstalling code for canister hello_rs, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai
The invocation to the wallet call forward method failed with the error: An error happened during the call: 5: Canister ryjl3-tyaaa-aaaaa-aaaba-cai trapped explicitly: Custom("No more values to deserialize")

sudo dfx canister install hello_rs -m=reinstall
Reinstalling code for canister hello_rs, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai
The invocation to the wallet call forward method failed with the error: An error happened during the call: 5: Canister ryjl3-tyaaa-aaaaa-aaaba-cai trapped explicitly: Custom("No more values to deserialize")

change the did file to:

service : (nat) -> {
  "increment": () -> ();
  "get": () -> (nat) query;
  "set": (nat) -> ();
}

and try dfx build --all

1 Like

I’m looking at the ‘incrementing a counter’ example and it looks like this may not be working again in 0.7.2.

Expected Results:

dfx canister call rust_counter get
(0)

Actual Results:

dfx canister call rust_counter get
[Canister ryjl3-tyaaa-aaaaa-aaaba-cai] Panicked at 'called `Option::unwrap()` on a `None` value'

Using debug prints I was able to confirm that init() is not called on canister startup.

Interesting behavior: when dfx.json has two canisters to deploy, init() IS called.

Hi @jboulineau!

I believe I need to clarify the canister lifecycle a bit.

  1. The first time you install canister code, the init function is called.
  2. If you decide to wipe out the old version of your canister and replace it with new code (by specifying --mode=reinstall option of the dfx canister install command), the init function is called after the new code is installed.
  3. If you upgrade your canister, the init function is not called. The system first calls canister_pre_upgrade function on the old version of the canister and then calls canister_post_upgrade on the new version.

This means that if you don’t specify upgrade hooks that use stable memory and upgrade the canister, all the state is lost. In case of something static mut TOKEN: Option<VotingToken> = None;, TOKEN becomes None again.

So my guess is that you upgraded your canister after the first installation without any upgrade hooks. This wiped away the initialized state, leading to this error. Re-installing the canister should fix the issue.

Interesting behavior: when dfx.json has two canisters to deploy, init() IS called.

Most likely, in this case a new canister was created, which triggered the (1) case in the lifecycle above.

6 Likes

Thanks @roman-kashitsyn! That was the missing piece for me.