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.

1 Like
  • #![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.

  • instant is a library to substitute for std::time::Instant. (It is not quite perfect because it has some behavior and API differences, but it is adequate for basic use cases like logging how long something took.)

6 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.

1 Like

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.