Skip to content

Commit a847529

Browse files
committed
docs: Add deep dive tutorial of src/lib.rs
Issue rustwasm#345
1 parent c3f91ca commit a847529

File tree

1 file changed

+125
-1
lines changed

1 file changed

+125
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,127 @@
11
# src/lib.rs
22

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

Comments
 (0)