From bcc94ace439c8fdabc3638f805784b3184a24698 Mon Sep 17 00:00:00 2001 From: Dan Wilhelm Date: Sun, 13 Jan 2019 02:23:46 -0800 Subject: [PATCH 1/3] docs: Update for template changes, typos --- .../tutorial/template-deep-dive/cargo-toml.md | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/docs/src/tutorial/template-deep-dive/cargo-toml.md b/docs/src/tutorial/template-deep-dive/cargo-toml.md index d6af282..3e26a48 100644 --- a/docs/src/tutorial/template-deep-dive/cargo-toml.md +++ b/docs/src/tutorial/template-deep-dive/cargo-toml.md @@ -3,11 +3,11 @@ `Cargo.toml` is the manifest file for Rust's package manager, `cargo`. This file contains metadata such as name, version, and dependencies for packages, which are call "crates" in Rust. -There's a bunch of metadata that the template gives us, but there are 3 key parts to discuss: +There's a bunch of metadata that the template gives us, but there are three key parts to discuss: -- [`crate-type`](#a1-crate-type) -- [`wasm-bindgen` dependency](#a2-wasm-bindgen-dependency) -- [`[features]` and `wee-alloc`, `console-error-panic-hook` dependencies](#a3-features-and-wee-alloc-console-error-panic-hook-dependencies) +1. [`crate-type`](#a1-crate-type) +2. [`wasm-bindgen` dependency](#a2-wasm-bindgen-dependency) +3. [`[features]` and `wee_alloc`, `console_error_panic_hook` dependencies](#a3-features-and-wee_alloc-console_error_panic_hook-dependencies)
@@ -15,7 +15,7 @@ There's a bunch of metadata that the template gives us, but there are 3 key part ```toml [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] ``` A Rust-`wasm` crate is a bit different from a normal crate, and as a result, we need to note @@ -33,6 +33,9 @@ as a dynamic library to be loaded from another language. In our case, we'll be c `.wasm` file, but this output type will create `*.so` files on Linux, `*.dylib` files on macOS, and `*.dll` files on Windows in non-`wasm` circumstances. +`#[crate_type = "rlib"]` signifies that an intermediate "Rust library" file will be produced. +This allows tests to use the main crate. + You can read more about linking and crate types, [here](https://doc.rust-lang.org/reference/linkage.html). ## 2. `wasm-bindgen` dependency @@ -52,23 +55,24 @@ We'll see more about how to use this library when we discuss what has been gener there is no `^` or `~` symbol- it looks like we're locking to the `0.2` version. However, that's not the case! In Rust, the `^` is implied. -## 3. `[features]` and `wee-alloc`, `console-error-panic-hook` dependencies +## 3. `[features]` and `wee_alloc`, `console_error_panic_hook` dependencies As part of our effort to design a template that helps people discover useful crates -for their particular use case, this template includes 2 dependencies that can be +for their particular use case, this template includes two dependencies that can be very useful for folks developing Rust-`wasm` crates: `console-error-panic-hook` and `wee-alloc`. -Because these dependencies are useful primarily in a specifc portion of the Rust-`wasm` +Because these dependencies are useful primarily in a specific portion of the Rust-`wasm` crate development workflow, we've also set up a bit of glue code that allows us to include -them both as dependences, but allowing for them to be optionally included. +them both as dependencies, but also allows them to be optionally included. ```toml [features] -default-features = ["console_error_panic_hook", "wee_alloc"] +default = ["console_error_panic_hook"] [dependencies] cfg-if = "0.1.2" +wasm-bindgen = "0.2" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -79,12 +83,22 @@ console_error_panic_hook = { version = "0.1.1", optional = true } # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size # compared to the default allocator's ~10K. It is slower than the default # allocator, however. -wee_alloc = { version = "0.4.1", optional = true } +# +# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +wee_alloc = { version = "0.4.2", optional = true } ``` -[`cfg-if`] allows us to check if certain features are enabled on a rust crate. We'll -use this crate in `utils.rs` to optionally enable `console_error_panic_hook` or -`wee_alloc`. By default, we have them enabled. To disable them, we can remove their -entry from the `default-features` vector. +[`cfg-if`] allows us to check if certain features are enabled on a Rust crate. We'll +use this crate later to optionally enable `console_error_panic_hook` or +`wee_alloc`. + +By default, only `console_error_panic_hook` is enabled. To disable either +feature, we can remove its name from the `default` vector. + +To learn more about these features, we discuss them in-depth in the `src/lib.rs` and +`src/utils.rs` sections. + +Briefly, they include: -To learn more about these features, we discuss them in depth in the `utils.rs` section. ++ **console_error_panic_hook** for logging panic messages to the developer console. ++ **wee_alloc**, an allocator optimized for small code size. From 8f22319d6fe88665d8507becced239b647914c4a Mon Sep 17 00:00:00 2001 From: Dan Wilhelm Date: Sun, 13 Jan 2019 02:24:31 -0800 Subject: [PATCH 2/3] docs: Add template deep dive for src/lib.rs Issue #345 --- .../tutorial/template-deep-dive/src-lib-rs.md | 126 +++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/docs/src/tutorial/template-deep-dive/src-lib-rs.md b/docs/src/tutorial/template-deep-dive/src-lib-rs.md index 1e31777..e5f72fe 100644 --- a/docs/src/tutorial/template-deep-dive/src-lib-rs.md +++ b/docs/src/tutorial/template-deep-dive/src-lib-rs.md @@ -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). \ No newline at end of file From 5205847d842d828470a9d37c1f423349370b417e Mon Sep 17 00:00:00 2001 From: Dan Wilhelm Date: Sun, 13 Jan 2019 02:25:42 -0800 Subject: [PATCH 3/3] docs: Add template deep dive for src/utils.rs Issue #346 --- .../template-deep-dive/src-utils-rs.md | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/docs/src/tutorial/template-deep-dive/src-utils-rs.md b/docs/src/tutorial/template-deep-dive/src-utils-rs.md index ea0c3c0..3ea5cb2 100644 --- a/docs/src/tutorial/template-deep-dive/src-utils-rs.md +++ b/docs/src/tutorial/template-deep-dive/src-utils-rs.md @@ -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). \ No newline at end of file