github.com/AR1011/wazero@v1.0.5/site/content/languages/rust.md (about) 1 +++ 2 title = "Rust" 3 +++ 4 5 ## Introduction 6 7 Beginning with 1.30 [Rust][1] can generate `%.wasm` files instead of 8 architecture-specific binaries through three targets: 9 10 - `wasm32-unknown-emscripten`: mostly for browser (JavaScript) use. 11 - `wasm32-unknown-unknown`: for standalone use in or outside the browser. 12 - `wasm32-wasi`: for use outside the browser. 13 14 This document is maintained by wazero, which is a WebAssembly runtime that 15 embeds in Go applications. Hence, our notes focus on targets used outside the 16 browser, tested by wazero: `wasm32-unknown-unknown` and `wasm32-wasi`. 17 18 This document also focuses on `rustc` as opposed to `cargo`, for precision and 19 brevity. 20 21 ## Overview 22 23 When Rust compiles a `%.rs` file with a `wasm32-*` target, the output `%.wasm` 24 depends on a subset of features in the [WebAssembly 1.0 Core specification] 25 ({{< ref "/specs#core" >}}). The [wasm32-wasi][15] target depends on [WASI] 26 ({{< ref "/specs#wasi" >}}) host functions as well. 27 28 Unlike some compilers, Rust also supports importing custom host functions and 29 exporting functions back to the host. 30 31 Here's a basic example of source in Rust: 32 33 ```rust 34 #[no_mangle] 35 pub extern "C" fn add(x: i32, y: i32) -> i32 { 36 x + y 37 } 38 ``` 39 40 The following is the minimal command to build a Wasm file. 41 42 ```bash 43 rustc -o lib.wasm --target wasm32-unknown-unknown --crate-type cdylib lib.rs 44 ``` 45 46 The resulting Wasm exports the `add` function so that the embedding host can 47 call it, regardless of if the host is written in Rust or not. 48 49 ### Digging Deeper 50 51 There are a few things to unpack above, so let's start at the top. 52 53 The rust source includes `#[no_mangle]` and `extern "C"`. Add these to 54 functions you want to export to the WebAssembly host. You can read more about 55 this in [The Embedded Rust Book][4]. 56 57 Next, you'll notice the flag `--crate-type cdylib` passed to `rustc`. This 58 compiles the source as a library, e.g. without a `main` function. 59 60 ## Disclaimer 61 62 This document includes notes contributed by the wazero community. While wazero 63 includes Rust examples, the community is less familiar with Rust. For more 64 help, consider the [Rust and WebAssembly book][5]. 65 66 Meanwhile, please help us [maintain][6] this document and [star our GitHub 67 repository][7], if it is helpful. Together, we can make WebAssembly easier on 68 the next person. 69 70 ## Constraints 71 72 Please read our overview of WebAssembly and 73 [constraints]({{< ref "_index.md#constraints" >}}). In short, expect 74 limitations in both language features and library choices when developing your 75 software. 76 77 The most common constraint is which crates you can depend on. Please refer to 78 the [Which Crates Will Work Off-the-Shelf with WebAssembly?][8] page in the 79 [Rust and WebAssembly book][5] for more on this. 80 81 ## Memory 82 83 When Rust compiles rust into Wasm, it configures the WebAssembly linear memory 84 to an initial size of 17 pages (1.1MB), and marks a position in that memory as 85 the heap base. All memory beyond that is used for the Rust heap. 86 87 Allocations within Rust (compiled to `%.wasm`) are managed as one would expect. 88 The `global_allocator` can allocate pages (`alloc_pages`) until `memory.grow` 89 on the host returns -1. 90 91 ### Host Allocations 92 93 Sometimes a host function needs to allocate memory directly. For example, to 94 write JSON of a given length before invoking an exported function to parse it. 95 96 The below snippet is a realistic example of a function exported to the host, 97 who needs to allocate memory first. 98 99 ```rust 100 #[no_mangle] 101 pub unsafe extern "C" fn configure(ptr: u32, len: u32) { 102 let json = &ptr_to_string(ptr, len); 103 } 104 ``` 105 106 Note: WebAssembly uses 32-bit memory addressing, so a `uintptr` is 32-bits. 107 108 The general flow is that the host allocates memory by calling an allocation 109 function with the size needed. Then, it writes data, in this case JSON, to the 110 memory offset (`ptr`). At that point, it can call a host function, ex 111 `configure`, passing the `ptr` and `size` allocated. The guest Wasm (compiled 112 from Rust) will be able to read the data. To ensure no memory leaks, the host 113 calls a free function, with the same `ptr`, afterwards and unconditionally. 114 115 Note: wazero includes an [example project][9] that shows this. 116 117 To allow the host to allocate memory, you need to define your own `malloc` and 118 `free` functions: 119 120 ```webassembly 121 (func (export "malloc") (param $size i32) (result (;$ptr;) i32)) 122 (func (export "free") (param $ptr i32) (param $size i32)) 123 ``` 124 125 The below implements this in Rustlang: 126 127 ```rust 128 use std::mem::MaybeUninit; 129 use std::slice; 130 131 unsafe fn ptr_to_string(ptr: u32, len: u32) -> String { 132 let slice = slice::from_raw_parts_mut(ptr as *mut u8, len as usize); 133 let utf8 = std::str::from_utf8_unchecked_mut(slice); 134 return String::from(utf8); 135 } 136 137 #[no_mangle] 138 pub extern "C" fn alloc(size: u32) -> *mut u8 { 139 // Allocate the amount of bytes needed. 140 let vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(size as usize); 141 142 // into_raw leaks the memory to the caller. 143 Box::into_raw(vec.into_boxed_slice()) as *mut u8 144 } 145 146 #[no_mangle] 147 pub unsafe extern "C" fn free(ptr: u32, size: u32) { 148 // Retake the pointer which allows its memory to be freed. 149 let _ = Vec::from_raw_parts(ptr as *mut u8, 0, size as usize); 150 } 151 ``` 152 153 As you can see, the above code is quite technical and should be kept separate 154 from your business logic as much as possible. 155 156 ## System Calls 157 158 Please read our overview of WebAssembly and 159 [System Calls]({{< ref "_index.md#system-calls" >}}). In short, WebAssembly is 160 a stack-based virtual machine specification, so operates at a lower level than 161 an operating system. 162 163 For functionality the operating system would otherwise provide, you must use 164 the `wasm32-wasi` target. This imports host functions in 165 [WASI]({{< ref "/specs#wasi" >}}). 166 167 For example, `rustc -o hello.wasm --target wasm32-wasi hello.rs` compiles the 168 below `main` function into a WASI function exported as `_start`. 169 170 ```rust 171 fn main() { 172 println!("Hello World!"); 173 } 174 ``` 175 176 Note: wazero includes an [example WASI project][10] including [source code][11] 177 that implements `cat` without any WebAssembly-specific code. 178 179 ## Concurrency 180 181 Please read our overview of WebAssembly and 182 [concurrency]({{< ref "_index.md#concurrency" >}}). In short, the current 183 WebAssembly specification does not support parallel processing. 184 185 ## Optimizations 186 187 Below are some commonly used configurations that allow optimizing for size or 188 performance vs defaults. Note that sometimes one sacrifices the other. 189 190 ### Binary size 191 192 Those with `%.wasm` binary size constraints can change their source or set 193 `rustc` flags to reduce it. 194 195 Source changes: 196 197 - [wee_alloc][12]: Smaller, WebAssembly-tuned memory allocator. 198 199 [`rustc` flags][13]: 200 201 - `-C debuginfo=0`: Strips DWARF, but retains the WebAssembly name section. 202 - `-C opt-level=3`: Includes all size optimizations. 203 204 Those using cargo should also use the `--release` flag, which corresponds to 205 `rustc -C debuginfo=0 -C opt-level=3`. 206 207 Those using the `wasm32-wasi` target should consider the [cargo-wasi][14] crate 208 as it dramatically reduces Wasm size. 209 210 ### Performance 211 212 Those with runtime performance constraints can change their source or set 213 `rustc` flags to improve it. 214 215 [`rustc` flags][13]: 216 217 - `-C opt-level=2`: Enable additional optimizations, frequently at the expense 218 of binary size. 219 220 ## Frequently Asked Questions 221 222 ### Why is my `%.wasm` binary so big? 223 224 Rust defaults can be overridden for those who can sacrifice features or 225 performance for a [smaller binary](#binary-size). After that, tuning your 226 source code may reduce binary size further. 227 228 [1]: https://www.rust-lang.org/tools/install 229 [4]: https://docs.rust-embedded.org/book/interoperability/rust-with-c.html#no_mangle 230 [5]: https://rustwasm.github.io/docs/book 231 [6]: https://github.com/AR1011/wazero/tree/main/site/content/languages/rust.md 232 [7]: https://github.com/AR1011/wazero/stargazers 233 [8]: https://rustwasm.github.io/docs/book/reference/which-crates-work-with-wasm.html 234 [9]: https://github.com/AR1011/wazero/tree/main/examples/allocation/rust 235 [10]: https://github.com/AR1011/wazero/tree/main/imports/wasi_snapshot_preview1/example 236 [11]: https://github.com/AR1011/wazero/tree/main/imports/wasi_snapshot_preview1/example/testdata/cargo-wasi 237 [12]: https://github.com/rustwasm/wee_alloc 238 [13]: https://doc.rust-lang.org/cargo/reference/profiles.html#profile-settings 239 [14]: https://github.com/bytecodealliance/cargo-wasi 240 [15]: https://github.com/rust-lang/rust/tree/1.68.0/library/std/src/sys/wasi