How to migrate ManualReply to ic_cdk v0.18

I don’t get how to adapt the deprecated ic_cdk::api::call::ManualReply of the ic_cdk v0.18 and I cannot find any description in the migration guide.

Has anyone an example to share?

For example, I would like to adapt:

#[update(guard = "caller_is_admin_controller", manual_reply = true)]
fn reject_proposal(proposal: RejectProposal) -> ManualReply<()> {
    match make_reject_proposal(&proposal) {
        Ok(_) => ManualReply::one(()),
        Err(e) => ManualReply::reject(e.to_string()),
    }
}

And the deprecation notice says Please use std::marker::PhantomData with manual_reply instead, so I tried following:

#[update(guard = "caller_is_admin_controller", manual_reply = true)]
fn reject_proposal(proposal: RejectProposal) -> PhantomData<()> {
    match make_reject_proposal(&proposal) {
        Ok(_) => msg_reply(()),
        Err(e) => msg_reject(e.to_string()),
    }

    PhantomData
}

but then I get the trait AsRef<[u8]> is not implemented for ()

The usage of PhantomData with manual_reply is correct.

The error probably came from msg_reply(()). The function signature is pub fn msg_reply<T: AsRef<[u8]>>(data: T). So to reply the call with empty bytes, you can invoke msg_reply(vec![]).

Thanks. But that’s not the same, isn’t it? Returning empty bytes is not the same as (), so wouldn’t this produce a different Candid response on the wire and therefore require changes to the definition and my clients to avoid issues (which is unlikely to happen)?

To reply the () value, you will need to encode it into bytes first.

let reply_bytes = candid::encode_one(()).unwrap(); 
ic_cdk::api::msg_reply(reply_bytes);

encode_one was the key!

I created a util for my code base that basically replicates the implementation of ManualReply and adds the encoded_one. CI passes and I did a quick manual test, therefore seems good to go.

PR if useful to anyone :backhand_index_pointing_right: junobuild/juno#1867

Thanks for the help!