Skip to content

Commit

Permalink
Merge pull request #3408 from dfinity/add-rust-limitations
Browse files Browse the repository at this point in the history
Add: Rust Wasm limitations doc
  • Loading branch information
jessiemongeon1 authored Aug 29, 2024
2 parents 53e3390 + d81ca5a commit 83d4c03
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
71 changes: 71 additions & 0 deletions docs/developer-docs/backend/rust/rust-limitations.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
keywords: [intermediate, rust, tutorial, limitations on wasm, rust wasm, rust limitations]
---

import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow";

# Rust limitations on Wasm

<MarkdownChipRow labels={["Intermediate", "Rust", "Tutorial"]} />

## Overview

WebAssembly (Wasm) is a binary code format and does not provide network access, a filesystem, or other functionalities other than pure computation. Anything function that needs to communicate outside of the Wasm module must use an IC API. This guide details limitations imposed by Wasm on canisters written in Rust.

## Wasm limitations

- You cannot create threads. Instead, use [`ic_cdk::spawn`](https://docs.rs/ic-cdk/0.16.0/ic_cdk/fn.spawn.html).
- You cannot sleep. Instead, use [`ic_cdk_timers`](https://docs.rs/ic-cdk-timers/latest/ic_cdk_timers/).
- You cannot use `Instant`. Instead, use [`ic_cdk::api::time`](https://docs.rs/ic-cdk/0.16.0/ic_cdk/api/fn.time.html).
- You cannot access environment variables with `std::env::var`, but you can embed compile-time environment variables with the `env!` macro.

## Crate limitations

Most crates work on ICP, but anything that performs input/output will not. Typically, it is fine to use a crate that performs input/output as long as you don't call the function that executes it. If you try to deploy a canister that uses this workflow, ICP will return an error about importing a function from `"env"`.

Crates that specifically have a Wasm mode usually do not work as expected, as they usually have a special mode for executing in the browser and will perform their functionality using JavaScript functions instead, which don’t exist on ICP. If you try to deploy a canister that uses this workflow, ICP will return an error about importing a function named `__wbindgen_describe`.

One example is the `getrandom` crate, which usually pulled in from another dependency. If you tried to import `uuid` with the `v4` feature, it refuses to compile into Wasm, but if you enable its Wasm mode, it just gives you a different error, because it’s trying to use JavaScript.

Instead, `getrandom` allows you to create your own random function with the `register_custom_getrandom!` macro. The following boilerplate will eliminate the error and allow other crates to fetch randomness like usual:

```rust
thread_local! {
// A global random number generator, seeded when the canister is initialized
static RNG: RefCell<Option<StdRng>> = RefCell::new(None);
}
#[init]
fn init() {
init_rng();
}
// Static variables are cleared on upgrade, so also initialize it on post-upgrade
#[post_upgrade]
fn post_upgrade() {
init_rng();
}
// Randomness on the IC is async, and the custom getrandom function can't be async,
// so we seed an RNG instead of always calling raw_rand directly
fn init_rng() {
// raw_rand is technically an inter-canister call, and you can't make those from lifecycle functions like #[init],
// so we schedule an immediate timer to make the call instead
ic_cdk_timers::set_timer(Duration::ZERO, || ic_cdk::spawn(async {
let (seed,) = ic_cdk::api::management_canister::main::raw_rand().await.unwrap();
// StdRng is from the `rand` crate. It makes for a good default but any RNG implementation would work
RNG.with(|rng| *rng.borrow_mut() = Some(StdRng::from_seed(seed.try_into().unwrap())));
}));
}
register_custom_getrandom!(custom_getrandom);
fn custom_getrandom(buf: &mut [u8]) -> Result<(), getrandom::Error> {
RNG.with(|rng| rng.borrow_mut().as_mut().unwrap().fill_bytes(buf));
Ok(())


```

Crates in the category [`no-std`](https://crates.io/categories/no-std) should typically work as expected.

Additional crate limitations include:

- You cannot use `tokio`, use [`ic_cdk::spawn`](https://docs.rs/ic-cdk/0.16.0/ic_cdk/fn.spawn.html).
- You cannot use crates that make or serve HTTP requests. Instead, use [HTTP outcalls](/docs/current/developer-docs/smart-contracts/advanced-features/https-outcalls/https-outcalls-overview) or the [gateway API](/docs/current/references/http-gateway-protocol-spec/).

1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,7 @@ hackathon: [
"developer-docs/backend/rust/rust-considerations",
"developer-docs/backend/rust/timers",
"developer-docs/backend/rust/stable-structures",
"developer-docs/backend/rust/rust-limitations",
"developer-docs/backend/rust/samples",
],
},
Expand Down

0 comments on commit 83d4c03

Please sign in to comment.