How to dynamically construct argument in rust

In my scenario, I need to construction argument dynamically to call another canister, and I don’t know the structure of the argument at compile time, I need to dynamically retrieve the structure of the argument from the configuration at runtime and construct the argument.

For example:

There is a canister with the following candid.


type Arg = record {

name: principal;

value: nat;

}

type Result = record {

r1: string;

r2: nat;

}

service : {

"func": (arg: Arg) -> Result;

}

I need to dynamically construct the Arg at runtime and call the func function.


// the configuration, which records the field name and field type of the request argument.

let config: Vec<Arg> = Vec::new();

// the map, which stores the request argument.

let mut map = HashMap::new();

// TODO Convert map to request argument according to the configuration

let arg = ...

let _call_result = ic_cdk::call(principal, funcName, (arg,)).await;

What am I supposed to do?

Thanks.

You need to define structs corresponding to Arg and Result in Rust.
Make them #derive[CandidType]. Then you can make the inter-canister call as the last line in your code.

Let me use some Rust CDK code to illustrate.
The IC management canister has its candid definition in the spec.
In Rust CDK, we define types for the arguments and results.

The Candid type

type canister_settings = record {
  controllers : opt vec principal;
  compute_allocation : opt nat;
  memory_allocation : opt nat;
  freezing_threshold : opt nat;
};

is translated to:

#[derive(
    CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
)]
pub struct CanisterSettings {
    /// A list of principals. Must be between 0 and 10 in size.
    pub controllers: Option<Vec<Principal>>,
    /// Must be a number between 0 and 100, inclusively.
    pub compute_allocation: Option<Nat>,
    /// Must be a number between 0 and 2^48^ (i.e 256TB), inclusively.
    pub memory_allocation: Option<Nat>,
    /// Must be a number between 0 and 2^64^-1, inclusively, and indicates a length of time in seconds.
    pub freezing_threshold: Option<Nat>,
}

Due to business needs, I don’t know another canister parameter type at the development stage, which needs to be generated dynamically at the runtime.

I found that with the candid library, it was possible to instantiate a argument of type IDLArgs, as shown in the following code, but how to convert an IDLArgs argument to an argument that could be used by the ic_sdk::call function

let str = r#"
   record { name = "name"; value = 1; }
"#;
let idl_args: IDLArgs = str.parse()?;
// How do I convert the idl_args of type IDLArgs to an args that can be used by ic_sdk::call
let args = ...
let _call_result = ic_cdk::call(principal, funcName, (args,)).await;

Thank you.

I don’t know another canister parameter type at the development stage, which needs to be generated dynamically at the runtime.

How do you get the type at the runtime then?

To serialize IDLArgs, see this doc: candid - Rust

Then you can use call_raw in ic_cdk::api::call - Rust to send the serialized bytes.

Thanks, using call_raw can solve my problem.