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