Compile Rust to target WASM, how to strip to 300Bytes?

Wen compiing the following Rust to target system WASM, I get a file of 57KB.

pub extern "C" fn transform(arlen: i32, ar: &mut [u8]) -> i32 {
    const ICHAR: u8 = 105;
    const OCHAR: u8 = 111;
       for i in 0..arlen as usize { if ar[i] == ICHAR { ar[i] = OCHAR; } }
    return arlen;

The cargo command being used is as follows:

cargo +nightly build --release --target wasm32-unknown-unknown

In contrast, compiling a similar C++ code to WASM with emscripten the resulting wasm-file has size 296 Bytes only.

I was able to strip down the Wasm file to ca 37KB using the tools wasm-gc and wasm-opt. Just, it seems that a lot of anonymous functions and constants remain in the wasm-file (see

Please, how could I get rid of the additional overhead of const-values in the Wasm file?

(The build-script/Makefile is available together with sources at

PS: The C++ reference has been added

There is a discussion of WASM code size in the article on the oxidation of source maps, you may find it useful if you do not know about it already.

I must admit, reading the article first time I missed the “code size” part at the end :wink:
AFAICS, regarding the final wasm-file, the article declares the contained const-data of the panic-dialog as a bug. The compiler rustc should not add these const-Data elements at all. Just, comparing the results from emcc and rustc, the compiler rustc is also adding a dependency to alloc-functionality (function $55 performing a “grow_memory”), and I don’t see any need for this.

tl;dr compile Rust code with opt-level="z" (nigthly), use wasm-gc, and then wasm-opt.


I tried opt-level=“z”, but result is similar, it does not eliminate the dependency to “alloc” functionality

Analyzing the the WAST from Rust generated WebAssembly the function call at the end of the “transform” function $0 is looking strange. Maybe this call is pulling-in the dependency to alloc and the const-data. (?)

Anybody out there who could explain hte reason for this call? Could I prevent this call, if I re-arrange my Rust-code?

(export "transform" (func $0))
 (func $0 (type $3) (param $var$0 i32) (param $var$1 i32) (result i32)
  (local $var$2 i32)
  (local $var$3 i32)
  (local $var$4 i32)
  (block $label$0
   (block $label$1
;;;;;; unexpected function call ;;;;;;;
  (call $68
   (i32.const 28)
   (get_local $var$1)
   (get_local $var$2)

Update: WASM file size is down to 186 Bytes :slight_smile:

Code available at repository

This has been achieved by using raw-pointers, getting rid of array-slice bound-checking.
Also using feature “wasm_import_memory” to import memory from calling environment; better for sandboxing.


pub extern "C" fn transform(arlen: i32, ar: *mut u8) -> i32 {
    const ICHAR: u8 = 105;
    const OCHAR: u8 = 111;
    for i in 0..arlen as usize {
      unsafe { if *ar.offset(i as isize) == ICHAR { *ar.offset(i as isize) = OCHAR; } }
    return arlen;

Still annoying are the ‘data’ elements, placing bytes into the linear memory at index 4, 12, and 28. Not sure what they are good for.

 (data (i32.const 4) "0\00\10\00")
 (data (i32.const 12) "\ff\ff\ff\ff\00\00\00\00\01\00\00\00\01\00\00\00")
 (data (i32.const 28) "\ff\ff\ff\ff\00\00\00\00\01\00\00\00\ff\ff\ff\ff")
1 Like