|
1 | 1 | # src/lib.rs
|
2 | 2 |
|
3 |
| -🚧 COMING SOON 🚧 |
| 3 | +`lib.rs` is the project's main source file. The name `lib.rs` commonly implies that this Rust project will be compiled as a library. |
| 4 | + |
| 5 | +It contains three key parts: |
| 6 | + |
| 7 | +1. [`#[wasm_bindgen]` functions](#a1-wasm_bindgen-functions) |
| 8 | +2. [Crate imports](#a2-crate-imports) |
| 9 | +3. [`wee_alloc` optional dependency](#a3-wee_alloc-optional-dependency) |
| 10 | + - [What is `wee_alloc`?](#a4-what-is-wee_alloc) |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +We'll start with the most important part of `lib.rs` -- the two `#[wasm_bindgen]` functions. In many cases, this is the only part of `lib.rs` you will need to modify. |
| 15 | + |
| 16 | +## 1. `#[wasm_bindgen]` functions |
| 17 | + |
| 18 | +The `#[wasm_bindgen]` attribute indicates that the function below it will be accessible both in JavaScript and Rust. |
| 19 | + |
| 20 | +```rust |
| 21 | +#[wasm_bindgen] |
| 22 | +extern { |
| 23 | + fn alert(s: &str); |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +The `extern` block imports the external JavaScript function `alert` into Rust. This declaration is required to call `alert` from Rust. By declaring it in this way, `wasm-bindgen` will create JavaScript stubs for `alert` which allow us to pass strings back and forth between Rust and JavaScript. |
| 28 | + |
| 29 | +We can see that the `alert` function requires a single parameter `s` of type `&str`, a string. In Rust, any string literal such as `"Hello, test-wasm!"` is of the type `&str`. So, `alert` could be called by writing `alert("Hello, test-wasm!");`. |
| 30 | + |
| 31 | +We knew to declare `alert` in this way because it is how we would call `alert` in JavaScript -- by passing in a string. |
| 32 | + |
| 33 | + |
| 34 | + |
| 35 | +```rust |
| 36 | +#[wasm_bindgen] |
| 37 | +pub fn greet() { |
| 38 | + alert("Hello, test-wasm!"); |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +If we were to write the `greet` function without the `#[wasm_bindgen]` attribute, then `greet` would not be easily accessible within JavaScript. Furthermore, we wouldn't be able to natively convert certain types between JavaScript and Rust such as `&str`. So, both the `#[wasm_bindgen]` attribute and the prior import of `alert` allow `greet` to be called from JavaScript. |
| 43 | + |
| 44 | +This is all you need to know to interface with JavaScript! If you are curious about the rest of the file, read on. |
| 45 | + |
| 46 | +## 2. Crate imports |
| 47 | + |
| 48 | +```rust |
| 49 | +extern crate cfg_if; |
| 50 | +extern crate wasm_bindgen; |
| 51 | +``` |
| 52 | + |
| 53 | +In `Cargo.toml`, we included the crates `cfg_if` and `wasm_bindgen` as project dependencies. |
| 54 | + |
| 55 | +Here, we explicitly declare that these crates will be used in `lib.rs`. |
| 56 | + |
| 57 | +```rust |
| 58 | +mod utils; |
| 59 | +``` |
| 60 | +This statement declares a new module named `utils` that is defined by the contents of `utils.rs`. In fact, we could equivalently replace the line with: |
| 61 | + |
| 62 | +```rust |
| 63 | +mod utils { |
| 64 | + // contents of utils.rs |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +In this case, the contents of `utils.rs` define a single public function `set_panic_hook`. Because we are placing it inside the `utils` module, we will be able to call the function directly by writing `utils::set_panic_hook()`. We will discuss its purpose in-depth in `src/utils.rs`. |
| 69 | + |
| 70 | + |
| 71 | +```rust |
| 72 | +use cfg_if::cfg_if; |
| 73 | +``` |
| 74 | + |
| 75 | +`use` allows us to conveniently refer to parts of a crate or module. For example, suppose the crate `cfg_if` contains a function `func`. It is always possible to call this function directly by writing `cfg_if::func()`. However, this is often tedious to write. If we specify `use cfg_if::func;`, then `func` can be called by just writing `func()` instead. |
| 76 | + |
| 77 | +With this in mind, the first `use` allows us to call the macro `cfg_if!` inside the crate `cfg_if` without writing `cfg_if::cfg_if!`. |
| 78 | + |
| 79 | +```rust |
| 80 | +use wasm_bindgen::prelude::*; |
| 81 | +``` |
| 82 | + |
| 83 | +Many modules contain a prelude, a list of things that should be automatically imported. This allows common features of the module to be conveniently accessed without a lengthy prefix. For example, in this file we can use `#[wasm_bindgen]` only because it is imported via the prelude. |
| 84 | + |
| 85 | +The asterisk at the end of this `use` indicates that everything inside the module `wasm_bindgen::prelude` can be referred to without prefixing it with `wasm_bindgen::prelude`. For example, `#[wasm_bindgen]` can also be written as `#[wasm_bindgen::prelude::wasm_bindgen]`, although this is not recommended. |
| 86 | + |
| 87 | +## 3. `wee_alloc` optional dependency |
| 88 | + |
| 89 | +```rust |
| 90 | +cfg_if! { |
| 91 | + if #[cfg(feature = "wee_alloc")] { |
| 92 | + extern crate wee_alloc; |
| 93 | + #[global_allocator] |
| 94 | + static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
| 95 | + } |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +This code block is intended to initialize `wee_alloc` as the global memory allocator, but only if the `wee_alloc` feature is enabled in `Cargo.toml`. |
| 100 | + |
| 101 | +We immediately notice that `cfg_if!` is a macro because it ends in `!`, similarly to other Rust macros such as `println!` and `vec!`. A macro is directly replaced by other code during compile time. |
| 102 | + |
| 103 | +During compile time, `cfg_if!` evaluates the `if` statement. This tests whether the feature `wee_alloc` is present in the `[features]` section of `Cargo.toml` (among other possible ways). |
| 104 | + |
| 105 | +As we saw earlier, the template only contains `"console_error_panic_hook"` and not `"wee_alloc"` in the `default` list. So, in this case, the `cfg_if!` block will be replaced by no code at all, and hence `wee_alloc` will not be used as the global allocator. |
| 106 | + |
| 107 | +```rust |
| 108 | +extern crate wee_alloc; |
| 109 | +#[global_allocator] |
| 110 | +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
| 111 | +``` |
| 112 | + |
| 113 | +If `"wee_alloc"` is appended to the `default` list in `Cargo.toml`, then the entire `cfg_if!` block is replaced with the contents of the `if` block, shown above. |
| 114 | + |
| 115 | +This code sets the `wee_alloc` allocator to be used as the global memory allocator. |
| 116 | + |
| 117 | +## 4. What is `wee_alloc`? |
| 118 | + |
| 119 | +Reducing the size of compiled WebAssembly code is important, since it is often transmitted over the Internet or placed on embedded devices. |
| 120 | + |
| 121 | +> `wee_alloc` is a tiny allocator designed for WebAssembly that has a (pre-compression) code-size footprint of only a single kilobyte. |
| 122 | +
|
| 123 | +[An analysis](http://fitzgeraldnick.com/2018/02/09/wee-alloc.html) suggests that over half of the bare minimum WebAssembly code is due to Rust's default memory allocator. Yet, WebAssembly code often just requires a couple of large intial allocations. |
| 124 | + |
| 125 | +`wee_alloc` trades off size for speed. Although it has a tiny code size footprint, it is relatively slow if additional allocations are needed. |
| 126 | + |
| 127 | +For more details, see the [`wee_alloc` repository](https://github.com/rustwasm/wee_alloc). |
0 commit comments