Deserialize to candid::Nat

I’ve also opened this up on the candid repo. Does anyone have experience using serde_json with candid::Nat with Rust? I can’t get a simple JSON string to deserialize to a Rust struct with a field that is of type candid::Nat.

Here’s a link to the issue with more information: Deserialize to candid::Nat · Issue #289 · dfinity/candid · GitHub

1 Like

Can you post the error message in the issue?

Without the error message, Im guessing Rust WASM doesn’t support arbitrary length integers? Nat8, Nat32, Nat64…

Error("invalid type: integer `100`, expected Nat value", line: 1, column: 33)

It’s a deserialization error, that’s what the candid::Nat type is for, since there is no native Rust representation of a candid Nat. The problem is getting serde to deserialize a JSON number to a candid::Nat.

1 Like

I see. I can add the visitor for integer. Nat currently only takes a specific internal representation for deserialization.

4 Likes

It would be amazing to allow any JSON number to be deserialized to candid::Nat!

Do you have an ETA on this? Just wondering if I should use something like #[serde(deserialize_with)] in the mean time. I assume I would just manually implement a visitor for this field for now, and then when the candid library is updated I can just remove that code. And our code would essentially be doing the same thing?

Can you try this branch: add more visitors for nat and int by chenyan-dfinity · Pull Request #290 · dfinity/candid · GitHub

I didn’t look at which integer type is used by serde_json, but I think it should be enough.

Thank you! I will try this branch and let you know how it goes

I’m trying to use that branch, but now my code won’t compile where it would compile before. Perhaps there are some issues with the ic-cdk version?

For this struct that worked with version 0.7.8 of candid:

#[derive(serde::Deserialize, candid::CandidType)]
        struct TransferRequest {
            from: String,
            to: String,
            amount: candid::Nat
        }

I get this error now: the trait bound `TransferRequest: ic_cdk::export::candid::CandidType` is not satisfied

If I change the struct to look like this:

#[derive(serde::Deserialize, ic_cdk::export::candid::CandidType)]
        struct TransferRequest {
            from: String,
            to: String,
            amount: candid::Nat
        }

Then I get the following error:

the trait `ic_cdk::export::candid::CandidType` is not implemented for `candid::Nat`

I’m using ic-cdk = "0.3.2"

1 Like

Right, the second code is correct. You will need to patch Cargo.toml.

[patch.crates-io.candid]
git = "https://github.com/dfinity/candid.git"
branch = "nat-visitor"
1 Like

Oh I see, should I not be installing candid directly? I’ve been installing candid and using candid::Nat and candid::CandidType. Should I be doing ic_cdk::export::candid:: always, generally speaking?

It all works!!! Thank you so much for getting this fixed and pointing me in the right direction. I will be doing a lot of deserialization in Rust for Azle GitHub - lastmjs/azle: JavaScript/TypeScript CDK for the Internet Computer, so if I run into any more issues maybe I’ll just post to this thread if the problems are similar

ic_cdk::export::candid is the current recommendation. This avoids candid version mismatch between ic-cdk and user’s import. Not sure if it’s the best way to handle version mismatch.

2 Likes

@chenyan Now I need to get my Rust structs serialized into JSON.

Imagine I have this struct:

#[derive(serde::Serialize, serde::Deserialize, ic_cdk::export::candid::CandidType)]
struct TransferRequest {
    nat: ic_cdk::export::candid::Nat
}

I am trying to take a value of type TransferRequest and serialize it into JSON like this:

serde_json::to_string(&transferRequest);

But I get the following error:

^^^^^^ the trait `Serialize` is not implemented for `ic_cdk::export::candid::Nat`

You know, the documentation here says I can just use u128 in Rust instead of candid::Nat…is that true? Supported types :: Internet Computer

Any best practices? Seems much easier to just use u128.

Serialize is not needed for Candid, so it’s not implemented for candid::Nat. You are right, you can use u128 for nat in Rust. The limitations are 1) u128 is always 128bit, while candid::Nat can take fewer space for smaller numbers; 2) You cannot decode numbers out of the 128bit range. Otherwise, they are identical.

1 Like