Rust best practice - don't panic after await - question

Hey,

I’ve just watched the talk about Best Practices for Canisters in Rust (Community Conversations | Best Practices for Canisters in Rust - YouTube and I have a question about the don’t panic after await best practice.

Here is the example from the slide:

#[update]
fn update_avatar(user_id: UserId, pic: ByteBuf) {
  let key = store_async(user_id, &pic)
  .await
  .unwrap();
  // BAD: if call fails, pic is likely to leak
  USERS.with(|users| set_avatar_key(user_id, key))
}

I understand it like this. If await is called then the method execution is suspended and all the previous local variables are stored on heap/stack and all this data is part of the commit point. When a reply is there then there are three cases:

  • Success reply: method continues to execute and at the end of the method all the local variables are deallocated from stack/heap.
  • Error reply (the called canister trapped): current canister panics and traps.
  • Success reply but the next synchronous call panics: the current canister traps like in the previous case.

In the last two cases memory leak can occure because after a trap the state is reverted to previous commit point - this is the state where the local variables (created before first await, see text above) are stored and this trap doesn’t deallocate them. Am I right, correct me if I’m wrong.

The questions are:

  • How to rewrite the code from above, so this leak dosn’t happen, even if panic occures after an await?
  • Can this type of leak also occure in Motoko after an await?

Thanks

  1. Don’t unwrap CallResults - handle them with :heart: through match.
  2. Consume values when you don’t need them anymore - pass pic and not &pic to the call.

By the way, this function misses async keyword.

I can’t say for sure for Motoko, but the GC is intended to solve this kind of problems.

3 Likes