Practical guides on no_std and wasm support

I have drafted a "Rust no_std Playbook" summarizing my (admittedly limited) experience on making Rust libraries no_std and WASM supports.

Would really appreciate correction and feedback from the Rust community.

  • #![cfg_attr(not(feature = "std"), no_std)] works but is not the best way to make an optionally-std crate. Instead use:

    #[cfg(feature = "std")]
    extern crate std;

    The advantage of this pattern is that you never get the std prelude items implicitly imported, so every use of std in your crate will be explicit; this makes the two cfg scenarios closer to each other, so it's easier to understand what's happening and to debug if you get something wrong.

  • Don't recommend using wee_alloc; it is buggy and unmaintained.

  • If you can make a general no_std crate, not just a Wasm-compatible crate, your test suite or CI should try building on a target that does not have std, so that the compilation success proves that neither your crate nor its dependencies links std. Just because your crate declares #[no_std] doesn't mean its dependencies all do.

    In addition to being “really no_std”, this also benefits Wasm builds by demonstrating that you won't hit any of the runtime panics in std when your code is run.

    One such target is thumbv7em-none-eabi; you already mentioned it, but only in passing in the context of matrix testing. I think this benefit should be mentioned more prominently.

  • web-time is a library to provide a substitute for std::time::Instant based on web APIs. (This bullet previously recommended instant, but that library is not being updated and does not match the API and behavior of std any more.)


I learnt that one the hard way... The way wee_alloc is implemented means you can get heap fragmentation really quickly. I ran into a situation where allocating a 1MB tensor followed by a a smaller allocation then dropping them in the same order in a loop caused my program to quickly OOM.


thank you Kevin for the really valuable feedback! I have updated the post with all of your suggestions.

a follow-up question, if wee_alloc is unmaintained, are there better light-weight allocator alternatives for WASM at the moment?

Using the default allocator costs less than 8k with LTO after stripping debuginfo. I don't see a point in trying to use a lighter allocator, especially when any smaller memory allocator almost certainly increases memory usage due to increased fragmentation.

pub fn foo() -> *mut u8 {
pub fn drop(a: *mut u8) {
    unsafe { Box::from_raw(a); }
$ rustc --target wasm32-unknown-unknown --crate-type cdylib -Copt-level=3 -Clto -Cstrip=debuginfo
$ ls -l
-rwxr-xr-x 1 bjorn3 bjorn3 7892  1 jan  1970 example.wasm

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.

An update to my previous recommendations: I no longer recommend the library instant. web-time does the same job, but better (more up to date with std's current API and behavior).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.