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