-
Notifications
You must be signed in to change notification settings - Fork 434
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3408 from dfinity/add-rust-limitations
Add: Rust Wasm limitations doc
- Loading branch information
Showing
2 changed files
with
72 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/). | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters