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).
At compile time this will test if the `wee_alloc` feature is enabled for this
compilation. If it's enabled we'll configure a global allocator (according to
[`wee_alloc`'s docs][wee-alloc-docs]), otherwise it'll compile to nothing.
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`.
@ -91,7 +94,10 @@ As we saw earlier, the `default` vector in `[features]` only contains `"console_
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.
Many crates contain a prelude, a list of things that are convenient to import
all at once. 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`.
@ -22,7 +22,6 @@ This allows us to write `cfg_if!` instead of `cfg_if::cfg_if!`, identically to t
```rust
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]
@ -31,36 +30,52 @@ cfg_if! {
}
```
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:
As described in the preceding section, this invocation of the `cfg_if!`
tests whether the `console_error_panic_hook` feature is enabled at compile time,
replacing with the statements in the `if` block or with those in the `else`
block. These two cases are:
```rust
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`.
This `use` statement means the function
`self::console_error_panic_hook::set_once` can now be accessed more conveniently
as `set_panic_hook`. With `pub`, this function will be accessible
outside of the `utils` module as `utils::set_panic_hook`.
```rust
#[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.
Here, `set_panic_hook` is defined to be an empty inline function. The inline
annotation here means that whenever the function is called the function call is
replaced with the body of the function, which is for `set_panic_hook` nothing!
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.
The [crate `console_error_panic_hook`][ceph] allows debugging Rust panic
messages in a web browser, making it much easier to debug WebAssembly code.
Let's compare error messages before and after enabling the feature:
Let's compare what happens when Rust code panics before and after enabling the
**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.
To do this, a [panic hook] is configured that logs panics to the
developer console via the JavaScript `console.error` function.
Note though that `console_error_panic_hook` is not entirely automatic, so you'll
need to make sure that `utils::set_panic_hook()` is called before any of our
code runs (and it's safe to run `set_panic_hook` many times).
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`
Reducing the size of compiled WebAssembly code is important, since it is often transmitted over the Internet or placed on embedded devices.
*Want to learn more about code sizein the rustwasm toolchain? Check out this [documentation](https://rustwasm.github.io/docs/book/reference/code-size.html).
WebAssembly code is frequently transmitted over the wire to users, so compiled
code size is often important to ensure an application loads quickly and is
responsive.
> `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.
`wee_alloc` trades off size for speed. It has a tiny code-size
footprint, but it is is not competitive in terms of performance with the
default global allocator, for example.
For even more details, see the [`wee_alloc` repository](https://github.com/rustwasm/wee_alloc).
For even more details, see the [`wee_alloc`
repository](https://github.com/rustwasm/wee_alloc), or
[general documentation](https://rustwasm.github.io/docs/book/reference/code-size.html) about
shrinking code size of WebAssembly binaries.
## Enabling `wee_alloc`
@ -25,32 +30,23 @@ In `lib.rs`, we have the configuration for `wee_alloc` inside a `cfg_if!` macro:
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`.
To do so we need to append `"wee_alloc"` to the `default` vector in `Cargo.toml`. Then, the `cfg_if!` block is replaced with the contents of the `if` block, shown above.
This code block is intended to initialize `wee_alloc` as the global memory
allocator, but only if the `wee_alloc` feature is enabled at compile time. The
feature can be enabled by passing extra options while building:
`wee_alloc` currently relies on features only available in Rust nightly. As such it requires you to use the nightly toolchain for compilation. If you have [Rustup](https://rustup.rs/) set up, you can install the nightly toolchain as follows:
```
rustup toolchain add nightly
$ wasm-pack build -- --features wee_alloc
```
To use `wasm-pack` with Rust nightly run:
or alternatively you could turn it on by default in `Cargo.toml`: