commit
ea99a2d343
@ -1,3 +1,127 @@ |
||||
# src/lib.rs |
||||
|
||||
🚧 COMING SOON 🚧 |
||||
`lib.rs` is the template's main source file. The name `lib.rs` commonly implies that this Rust project will be compiled as a library. |
||||
|
||||
It contains three key parts: |
||||
|
||||
1. [`#[wasm_bindgen] functions`](#a1-wasm_bindgen-functions) |
||||
2. [Crate imports](#a2-crate-imports) |
||||
3. [`wee_alloc` optional dependecy](#a3-wee_alloc-optional-dependecy) |
||||
- [What is `wee_alloc`?](#what-is-wee_alloc) |
||||
|
||||
--- |
||||
|
||||
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. |
||||
|
||||
## 1. `#[wasm_bindgen]` functions |
||||
|
||||
The `#[wasm_bindgen]` attribute indicates that the function below it will be accessible both in JavaScript and Rust. |
||||
|
||||
```rust |
||||
#[wasm_bindgen] |
||||
extern { |
||||
fn alert(s: &str); |
||||
} |
||||
``` |
||||
|
||||
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. |
||||
|
||||
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 type `&str`. So, `alert` could be called by writing `alert("Hello, test-wasm!");`. |
||||
|
||||
We knew to declare `alert` in this way because it is how we would call `alert` in JavaScript -- by passing it a string argument. |
||||
|
||||
```rust |
||||
#[wasm_bindgen] |
||||
pub fn greet() { |
||||
alert("Hello, test-wasm!"); |
||||
} |
||||
``` |
||||
|
||||
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 such as `&str` between JavaScript and Rust. So, both the `#[wasm_bindgen]` attribute and the prior import of `alert` allow `greet` to be called from JavaScript. |
||||
|
||||
This is all you need to know to interface with JavaScript! If you are curious about the rest, read on. |
||||
|
||||
## 2. Crate imports |
||||
|
||||
```rust |
||||
extern crate cfg_if; |
||||
extern crate wasm_bindgen; |
||||
``` |
||||
|
||||
In `Cargo.toml`, we included the crates `cfg_if` and `wasm_bindgen` as project dependencies. |
||||
|
||||
Here, we explicitly declare that these crates will be used in `lib.rs`. |
||||
|
||||
```rust |
||||
mod utils; |
||||
``` |
||||
This statement declares a new module named `utils` that is defined by the contents of `utils.rs`. Equivalently, we could place the contents of `utils.rs` inside the `utils` declaration, replacing the line with: |
||||
|
||||
```rust |
||||
mod utils { |
||||
// contents of utils.rs |
||||
} |
||||
``` |
||||
|
||||
Either way, 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 how and why to use this function in `src/utils.rs`. |
||||
|
||||
|
||||
```rust |
||||
use cfg_if::cfg_if; |
||||
``` |
||||
|
||||
`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 first specify `use cfg_if::func;`, then `func` can be called by just writing `func()` instead. |
||||
|
||||
With this in mind, this `use` allows us to call the macro `cfg_if!` inside the crate `cfg_if` without writing `cfg_if::cfg_if!`. |
||||
|
||||
```rust |
||||
use wasm_bindgen::prelude::*; |
||||
``` |
||||
|
||||
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 brought into scope by the prelude. |
||||
|
||||
The asterisk at the end of this `use` indicates that everything inside the module `wasm_bindgen::prelude` (i.e. the module `prelude` inside the crate `wasm_bindgen`) can be referred to without prefixing it with `wasm_bindgen::prelude`. |
||||
|
||||
For example, `#[wasm_bindgen]` could also be written as `#[wasm_bindgen::prelude::wasm_bindgen]`, although this is not recommended. |
||||
|
||||
## 3. `wee_alloc` optional dependecy |
||||
|
||||
```rust |
||||
cfg_if! { |
||||
if #[cfg(feature = "wee_alloc")] { |
||||
extern crate wee_alloc; |
||||
#[global_allocator] |
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
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`. |
||||
|
||||
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. |
||||
|
||||
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 to set it). |
||||
|
||||
As we saw earlier, the `default` vector in `[features]` only contains `"console_error_panic_hook"` and not `"wee_alloc"`. So, in this case, the `cfg_if!` block will be replaced by no code at all, and hence the default memory allocator will be used instead of `wee_alloc`. |
||||
|
||||
```rust |
||||
extern crate wee_alloc; |
||||
#[global_allocator] |
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
||||
``` |
||||
|
||||
However, suppose `"wee_alloc"` is appended to the `default` vector in `Cargo.toml`. Then, the `cfg_if!` block is instead replaced with the contents of the `if` block, shown above. |
||||
|
||||
This code sets the `wee_alloc` allocator to be used as the global memory allocator. |
||||
|
||||
### What is `wee_alloc`? |
||||
|
||||
Reducing the size of compiled WebAssembly code is important, since it is often transmitted over the Internet or placed on embedded devices. |
||||
|
||||
> `wee_alloc` is a tiny allocator designed for WebAssembly that has a (pre-compression) code-size footprint of only a single kilobyte. |
||||
|
||||
[An analysis](http://fitzgeraldnick.com/2018/02/09/wee-alloc.html) suggests that over half of the bare minimum WebAssembly memory footprint is required by Rust's default memory allocator. Yet, WebAssembly code often does not require a sophisticated allocator, since it often just requests a couple of large initial allocations. |
||||
|
||||
`wee_alloc` trades off size for speed. Although it has a tiny code-size footprint, it is relatively slow if additional allocations are needed. |
||||
|
||||
For more details, see the [`wee_alloc` repository](https://github.com/rustwasm/wee_alloc). |
@ -1,3 +1,66 @@ |
||||
# src/utils.rs |
||||
|
||||
🚧 COMING SOON 🚧 |
||||
The purpose of `utils.rs` is to define the `utils` module, which contains a single function `set_panic_hook`. This function becomes part of the `utils` module in `lib.rs`, as described in the preceding section. |
||||
|
||||
If the `console_error_panic_hook` feature is not enabled, then `set_panic_hook` is defined to be an inlined empty function. So, there is no run-time performance or code-size penalty incurred by its use. |
||||
|
||||
We will discuss: |
||||
1. [Defining `set_panic_hook`](#a1-defining-set_panic_hook) |
||||
2. [What is `console_error_panic_hook`?](#a2-what-is-console_error_panic_hook) |
||||
|
||||
|
||||
--- |
||||
|
||||
## 1. Defining `set_panic_hook` |
||||
|
||||
``` |
||||
use cfg_if::cfg_if; |
||||
``` |
||||
|
||||
This allows us to write `cfg_if!` instead of `cfg_if::cfg_if!`, identically to the line in `src/lib.rs`. |
||||
|
||||
``` |
||||
cfg_if! { |
||||
if #[cfg(feature = "console_error_panic_hook")] { |
||||
extern crate console_error_panic_hook; |
||||
pub use self::console_error_panic_hook::set_once as set_panic_hook; |
||||
} else { |
||||
#[inline] |
||||
pub fn set_panic_hook() {} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
As described in the preceding section, the macro `cfg_if!` evaluates the `if` statement during compile time. This is possible because it is essentially testing whether `"console_error_panic_hook"` is defined in the `[features]` section of `Cargo.toml`, which is available during compile time. |
||||
|
||||
The entire macro block will either be replaced with the statements in the `if` block or with those in the `else` block. These two cases are now described in turn: |
||||
|
||||
``` |
||||
extern crate console_error_panic_hook; |
||||
pub use self::console_error_panic_hook::set_once as set_panic_hook; |
||||
``` |
||||
|
||||
Due to the `use` statement, the function `self::console_error_panic_hook::set_once` can now be accessed more conveniently as `set_panic_hook`. Due to `pub`, this function will be publicly accessible outside of the `utils` module as `utils::set_panic_hook`. |
||||
|
||||
``` |
||||
#[inline] |
||||
pub fn set_panic_hook() {} |
||||
``` |
||||
|
||||
An inline function replaces the function call with the contents of the function during compile time. Here, `set_panic_hook` is defined to be an empty inline function. This allows the use of `set_panic_hook` without any run-time or code-size performance penalty if the feature is not enabled. |
||||
|
||||
## 2. What is `console_error_panic_hook`? |
||||
|
||||
The crate `console_error_panic_hook` enhances error messages in the web browser. This allows you to easily debug WebAssembly code. |
||||
|
||||
Let's compare error messages before and after enabling the feature: |
||||
|
||||
**Before:** `"RuntimeError: Unreachable executed"` |
||||
|
||||
**After:** `"panicked at 'index out of bounds: the len is 3 but the index is 4', libcore/slice/mod.rs:2046:10"` |
||||
|
||||
To do this, a panic hook for WebAssembly is provided that logs panics to the developer console via the JavaScript `console.error` function. |
||||
|
||||
Note that although the template sets up the function, your error messages will not automatically be enhanced. To enable the enhanced errors, call the function `utils::set_panic_hook()` in `lib.rs` when your code first runs. The function may be called multiple times if needed. |
||||
|
||||
For more details, see the [`console_error_panic_hook` repository](https://github.com/rustwasm/console_error_panic_hook). |
Loading…
Reference in new issue