wa-lang.org/wazero@v1.0.2/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` 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