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.

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

    #![no_std]
    
    #[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.)

11 Likes

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.

2 Likes

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.

#[no_mangle]
pub fn foo() -> *mut u8 {
    Box::into_raw(Box::new(1))
}
#[no_mangle]
pub fn drop(a: *mut u8) {
    unsafe { Box::from_raw(a); }
}
$ rustc example.rs --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
2 Likes

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