├── .github ├── ISSUE_TEMPLATE │ ├── ----misleading-unclear-docs.md │ ├── ---bug-report.md │ ├── ---feature-request.md │ └── ---other.md └── pull_request_template.md ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── _theme └── header.hbs ├── book.toml └── src ├── SUMMARY.md ├── background-and-concepts.md ├── game-of-life ├── code-size.md ├── debugging.md ├── deploying-to-production.md ├── hello-world.md ├── implementing.md ├── interactivity.md ├── introduction.md ├── publishing-to-npm.md ├── rules.md ├── setup.md ├── testing.md └── time-profiling.md ├── images ├── game-of-life │ ├── bench-perf-annotate.png │ ├── bench-perf-report.png │ ├── console-time-in-profiler.png │ ├── console-time-in-universe-tick.png │ ├── console-time.png │ ├── debugging.png │ ├── drawCells-after-flamegraph.png │ ├── drawCells-after-waterfall.png │ ├── drawCells-before-calltree.png │ ├── drawCells-before-flamegraph.png │ ├── drawCells-before-waterfall.png │ ├── hello-world.png │ ├── initial-game-of-life-pre.png │ ├── initial-game-of-life.png │ ├── initial-universe.png │ ├── next-universe.png │ ├── perf-report.png │ ├── profiler-firefox-show-gecko-platform.png │ ├── profiler-with-rust-names.png │ ├── universe.png │ └── waterfall-after-branches-and-unrolling.png └── wasm_hello_world_screenshot.png ├── introduction.md ├── reference ├── add-wasm-support-to-crate.md ├── code-size.md ├── crates.md ├── debugging.md ├── deploying-to-production.md ├── index.md ├── js-ffi.md ├── project-templates.md ├── time-profiling.md ├── tools.md └── which-crates-work-with-wasm.md ├── setup.md ├── what-is-webassembly.md └── why-rust-and-webassembly.md /.github/ISSUE_TEMPLATE/----misleading-unclear-docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4D1❌ Misleading/Unclear Docs" 3 | about: Point out documentation that needs to be improved! 4 | 5 | --- 6 | 7 | **Where in the docs did you come across this?** 8 | 9 | **Describe what about it does not make sense** 10 | 11 | **Why does it not make sense?** 12 | 13 | **How could we improve it?** 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug report" 3 | about: Create a report for broken code in the docs! 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4DD Feature request" 3 | about: Suggest an idea for the book! 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4AD Other" 3 | about: For issues that don't fall into the other categories 4 | 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ✋ A similar PR may already be submitted! 2 | Please search 🔎 among the [open pull requests][open-prs] before creating one. 3 | 4 | Updating the Game of Life tutorial's code? Also send a PR to 5 | [`rustwasm/wasm_game_of_life`!](https://github.com/rustwasm/wasm_game_of_life) 6 | 7 | Now that you've checked, it's time to create your PR. 📝 8 | Thanks for submitting! 🙏 9 | 10 | For more information, see the [contributing guide][contributing]. 👫 11 | 12 | ### Summary 13 | 14 | Explain the **motivation** for making this change. What existing problem does the pull request solve? 🤔 15 | 16 | 17 | Fixes #___ 18 | 19 | [contributing]: https://github.com/rustwasm/book/blob/master/CONTRIBUTING.md 20 | [open-prs]: https://github.com/rustwasm/book/pulls 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book/* 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | # before_install: 4 | # - curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.2.3/mdbook-v0.2.3-x86_64-unknown-linux-musl.tar.gz | tar xzf - 5 | # - export PATH=$PATH:`pwd` 6 | 7 | # Temporarily disable using pre-built binaries until 8 | # https://github.com/rust-lang-nursery/mdBook/issues/820 is fixed. 9 | before_script: 10 | - cargo install mdbook --version 0.2.3 11 | 12 | script: 13 | - mv _theme theme 14 | - mdbook build 15 | 16 | deploy: 17 | provider: script 18 | script: curl -LsSf https://git.io/fhJ8n | rustc - && (cd book && ../rust_out) 19 | skip_cleanup: true 20 | on: 21 | branch: master 22 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at aturon@mozilla.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We're so glad you're interested in contributing to Rust! You're in the right 4 | place if you want to make your own contributions to the `rustwasm/book` project. 5 | If you have an idea for making this document or anything else contained in the 6 | `rustwasm/book` project more useful, you can help by following the steps below. 7 | 8 | ## Filing an Issue 9 | 10 | If you're using this `book` and come across out of date information, examples 11 | that don't work, or anything else that's wrong, you can help by filing an issue. 12 | 13 | First, review the [Code of Conduct][coc]. Then check the 14 | [open issues][open-issues] to see if someone is having 15 | the same problem as you. If they are, and you have an idea for how to fix the problem, 16 | skip to the section below about submitting a PR! If you don't, please chime in on the 17 | issue comment thread so that the people working on the project have a better sense of 18 | how many people are experiencing the issue. 19 | 20 | This is a documentation and education project, so discussion about how to improve 21 | the experience for beginning users are particularly welcome. We greatly value the 22 | perspective of new users. If you found something was confusing please file an issue 23 | [here][new-issues]. Please fill out the template to give the 24 | people addressing your issue as much information as possible. 25 | 26 | 27 | ## Submitting a PR 28 | 29 | Pull requests are welcome to address all issues, whether you filed them or not! If 30 | you have an idea for contributing or a problem you want to solve, please do 31 | go through the process outlined above for opening an issue before you open 32 | a pull request. It helps everyone to discuss progress on the repo collaboratively, 33 | and to avoid duplicate work. 34 | 35 | ## Conduct 36 | 37 | This project is a part of the [`rust-wasm` working group][rust-wg],an official working 38 | group of the Rust project. We follow the Rust 39 | [Code of Conduct and enforcement policies][coc]. 40 | 41 | 42 | #### Thanks 43 | 44 | We're excited that you're interested in Rust and WebAssembly! Thank you for 45 | helping us make information about these technologies available to everyone. We 46 | welcome more contributions from you. 47 | 48 | [rust-wg]: https://github.com/rustwasm/team 49 | [coc]: https://github.com/rustwasm/book/blob/master/CODE_OF_CONDUCT.md 50 | [new-issues]: https://github.com/rustwasm/book/issues/new 51 | [open-issues]: https://github.com/rustwasm/book/issues 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aaron Turon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Rust and WebAssembly Book 2 | 3 | This repo contains documentation on using Rust for wasm, common workflows, how 4 | to get started and more. It acts as a guide for how to do some neat things with 5 | it. 6 | 7 | If you would like to start learning how to use Rust and WebAssembly together 8 | immediately, you can read the book [online here][book]. 9 | 10 | [Open issues for improving the Rust and WebAssembly book.][book-issues] 11 | 12 | [book-issues]: https://github.com/rustwasm/book/issues 13 | 14 | ## Building the Book 15 | 16 | The book is made using [`mdbook`][mdbook]. To install it you'll need `cargo` 17 | installed. If you don't have any Rust tooling installed, you'll need to install 18 | [`rustup`][rustup] first. Follow the instructions on the site in order to get 19 | setup. 20 | 21 | Once you have that done then just do the following: 22 | 23 | ```bash 24 | $ cargo install mdbook 25 | ``` 26 | 27 | Make sure the `cargo install` directory is in your `$PATH` so that you can run 28 | the binary. 29 | 30 | Now just run this command from this directory: 31 | 32 | ```bash 33 | $ mdbook build 34 | ``` 35 | 36 | This will build the book and output files into a directory called `book`. From 37 | there you can navigate to the `index.html` file to view it in your browser. You 38 | could also run the following command to automatically generate changes if you 39 | want to look at changes you might be making to it: 40 | 41 | ```bash 42 | $ mdbook serve 43 | ``` 44 | 45 | This will automatically generate the files as you make changes and serves them 46 | locally so you can view them easily without having to call `build` every time. 47 | 48 | The files are all written in Markdown so if you don't want to generate the book 49 | to read them then you can read them from the `src` directory. 50 | 51 | [mdbook]: https://github.com/rust-lang-nursery/mdBook 52 | [rustup]: https://github.com/rust-lang-nursery/rustup.rs/ 53 | [book]: https://rustwasm.github.io/book/game-of-life/introduction.html 54 | -------------------------------------------------------------------------------- /_theme/header.hbs: -------------------------------------------------------------------------------- 1 | 37 |
38 | This is unpublished documentation of 39 | working with Rust and WebAssembly, the published documentation is available 40 | 41 | on the main Rust and WebAssembly documentation site 42 | . Features documented here may not be available in released versions of 43 | tooling for Rust and WebAssembly. 44 |
45 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rust and WebAssembly" 3 | author = "The Rust and WebAssembly Working Group" 4 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 3 | - [イントロダクション](./introduction.md) 4 | - [なぜRust and WebAssembly?](./why-rust-and-webassembly.md) 5 | - [背景とコンセプト](./background-and-concepts.md) 6 | - [WebAssemblyとは?](./what-is-webassembly.md) 7 | -------------------------------------------------------------------------------- 8 | - [チュートリアル](./game-of-life/introduction.md) 9 | - [セットアップ](./game-of-life/setup.md) 10 | - [Hello, World!](./game-of-life/hello-world.md) 11 | - [Rules](./game-of-life/rules.md) 12 | - [Implementing Life](./game-of-life/implementing.md) 13 | - [Testing Life](./game-of-life/testing.md) 14 | - [Debugging](./game-of-life/debugging.md) 15 | - [Adding Interactivity](./game-of-life/interactivity.md) 16 | - [Time Profiling](./game-of-life/time-profiling.md) 17 | - [Shrinking `.wasm` Size](./game-of-life/code-size.md) 18 | - [Publishing to npm](./game-of-life/publishing-to-npm.md) 19 | -------------------------------------------------------------------------------- 20 | - [Reference](./reference/index.md) 21 | - [Crates You Should Know](./reference/crates.md) 22 | - [Tools You Should Know](./reference/tools.md) 23 | - [Project Templates](./reference/project-templates.md) 24 | - [Debugging](./reference/debugging.md) 25 | - [Time Profiling](./reference/time-profiling.md) 26 | - [Shrinking `.wasm` Size](./reference/code-size.md) 27 | - [JavaScript Interoperation](./reference/js-ffi.md) 28 | - [Which Crates Will Work Off-the-Shelf with WebAssembly?](./reference/which-crates-work-with-wasm.md) 29 | - [How to Add WebAssembly Support to a General-Purpose Crate](./reference/add-wasm-support-to-crate.md) 30 | - [Deploying Rust and WebAssembly to Production](./reference/deploying-to-production.md) 31 | -------------------------------------------------------------------------------- /src/background-and-concepts.md: -------------------------------------------------------------------------------- 1 | # 背景とコンセプト 2 | 3 | 4 | 5 | この節はRust and WebAssemblyの開発に入っていくために必要な文脈を提供します。 6 | 7 | 9 | -------------------------------------------------------------------------------- /src/game-of-life/code-size.md: -------------------------------------------------------------------------------- 1 | # Shrinking `.wasm` Size 2 | 3 | For `.wasm` binaries that we ship to clients over the network, such as our Game 4 | of Life Web application, we want to keep an eye on code size. The smaller our 5 | `.wasm` is, the faster our page loads get, and the happier our users are. 6 | 7 | ## How small can we get our Game of Life `.wasm` binary via build configuration? 8 | 9 | [Take a moment to review the build configuration options we can tweak to get 10 | smaller `.wasm` code 11 | sizes.](../reference/code-size.html#optimizing-builds-for-code-size) 12 | 13 | With the default release build configuration (without debug symbols), our 14 | WebAssembly binary is 29,410 bytes: 15 | 16 | ``` 17 | $ wc -c pkg/wasm_game_of_life_bg.wasm 18 | 29410 pkg/wasm_game_of_life_bg.wasm 19 | ``` 20 | 21 | After enabling LTO, setting `opt-level = "z"`, and running `wasm-opt -Oz`, the 22 | resulting `.wasm` binary shrinks to only 17,317 bytes: 23 | 24 | ``` 25 | $ wc -c pkg/wasm_game_of_life_bg.wasm 26 | 17317 pkg/wasm_game_of_life_bg.wasm 27 | ``` 28 | 29 | And if we compress it with `gzip` (which nearly every HTTP server does) we get 30 | down to a measly 9,045 bytes! 31 | 32 | ``` 33 | $ gzip -9 < pkg/wasm_game_of_life_bg.wasm | wc -c 34 | 9045 35 | ``` 36 | 37 | ## Exercises 38 | 39 | * Use [the `wasm-snip` tool](../reference/code-size.html#use-the-wasm-snip-tool) 40 | to remove the panicking infrastructure functions from our Game of Life's 41 | `.wasm` binary. How many bytes does it save? 42 | 43 | * Build our Game of Life crate with and without [`wee_alloc` as its global 44 | allocator](https://github.com/rustwasm/wee_alloc). The 45 | `rustwasm/wasm-pack-template` template that we cloned to start this project 46 | has a "wee_alloc" cargo feature that you can enable by adding it to the 47 | `default` key in the `[features]` section of `wasm-game-of-life/Cargo.toml`: 48 | 49 | ```toml 50 | [features] 51 | default = ["wee_alloc"] 52 | ``` 53 | 54 | How much size does using `wee_alloc` shave off of the `.wasm` 55 | binary? 56 | 57 | * We only ever instantiate a single `Universe`, so rather than providing a 58 | constructor, we can export operations that manipulate a single `static mut` 59 | global instance. If this global instance also uses the double buffering 60 | technique discussed in earlier chapters, we can make those buffers also be 61 | `static mut` globals. This removes all dynamic allocation from our Game of 62 | Life implementation, and we can make it a `#![no_std]` crate that doesn't 63 | include an allocator. How much size was removed from the `.wasm` by completely 64 | removing the allocator dependency? 65 | -------------------------------------------------------------------------------- /src/game-of-life/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | Before we write much more code, we will want to have some debugging tools in our 4 | belt for when things go wrong. Take a moment to review the [reference page 5 | listing tools and approaches available for debugging Rust-generated 6 | WebAssembly][reference-debugging]. 7 | 8 | [reference-debugging]: ../reference/debugging.html 9 | 10 | ## Enable Logging for Panics 11 | 12 | [If our code panics, we want informative error messages to appear in the 13 | developer console.](../reference/debugging.html#logging-panics) 14 | 15 | Our `wasm-pack-template` comes with an optional, enabled-by-default dependency 16 | on [the `console_error_panic_hook` crate][panic-hook] that is configured in 17 | `wasm-game-of-life/src/utils.rs`. All we need to do is install the hook in an 18 | initialization function or common code path. We can call it inside the 19 | `Universe::new` constructor in `wasm-game-of-life/src/lib.rs`: 20 | 21 | ```rust 22 | pub fn new() -> Universe { 23 | utils::set_panic_hook(); 24 | 25 | // ... 26 | } 27 | ``` 28 | 29 | [panic-hook]: https://github.com/rustwasm/console_error_panic_hook 30 | 31 | ## Add Logging to our Game of Life 32 | 33 | Let's [use the `console.log` function via the `web-sys` crate to add some 34 | logging][logging] about each cell in our `Universe::tick` function. 35 | 36 | First, add `web-sys` as a dependency and enable its `"console"` feature in 37 | `wasm-game-of-life/Cargo.toml`: 38 | 39 | ```toml 40 | [dependencies.web-sys] 41 | version = "0.3" 42 | features = [ 43 | "console", 44 | ] 45 | ``` 46 | 47 | For ergonomics, we'll wrap the `console.log` function up in a `println!`-style 48 | macro: 49 | 50 | [logging]: ../reference/debugging.html#logging-with-the-console-apis 51 | 52 | ```rust 53 | extern crate web_sys; 54 | 55 | // A macro to provide `println!(..)`-style syntax for `console.log` logging. 56 | macro_rules! log { 57 | ( $( $t:tt )* ) => { 58 | web_sys::console::log_1(&format!( $( $t )* ).into()); 59 | } 60 | } 61 | ``` 62 | 63 | Now, we can start logging messages to the console by inserting calls to `log` in 64 | Rust code. For example, to log each cell's state, live neighbors count, and next 65 | state, we could modify `wasm-game-of-life/src/lib.rs` like this: 66 | 67 | ```diff 68 | diff --git a/src/lib.rs b/src/lib.rs 69 | index f757641..a30e107 100755 70 | --- a/src/lib.rs 71 | +++ b/src/lib.rs 72 | @@ -123,6 +122,14 @@ impl Universe { 73 | let cell = self.cells[idx]; 74 | let live_neighbors = self.live_neighbor_count(row, col); 75 | 76 | + log!( 77 | + "cell[{}, {}] is initially {:?} and has {} live neighbors", 78 | + row, 79 | + col, 80 | + cell, 81 | + live_neighbors 82 | + ); 83 | + 84 | let next_cell = match (cell, live_neighbors) { 85 | // Rule 1: Any live cell with fewer than two live neighbours 86 | // dies, as if caused by underpopulation. 87 | @@ -140,6 +147,8 @@ impl Universe { 88 | (otherwise, _) => otherwise, 89 | }; 90 | 91 | + log!(" it becomes {:?}", next_cell); 92 | + 93 | next[idx] = next_cell; 94 | } 95 | } 96 | ``` 97 | 98 | ## Using a Debugger to Pause Between Each Tick 99 | 100 | [Browser's stepping debuggers are useful for inspecting the JavaScript that our 101 | Rust-generated WebAssembly interacts 102 | with.](../reference/debugging.html#using-a-debugger) 103 | 104 | For example, we can use the debugger to pause on each iteration of our 105 | `renderLoop` function by placing [a JavaScript `debugger;` statement][dbg-stmt] 106 | above our call to `universe.tick()`. 107 | 108 | ```js 109 | const renderLoop = () => { 110 | debugger; 111 | universe.tick(); 112 | 113 | drawGrid(); 114 | drawCells(); 115 | 116 | requestAnimationFrame(renderLoop); 117 | }; 118 | ``` 119 | 120 | This provides us with a convenient checkpoint for inspecting logged messages, 121 | and comparing the currently rendered frame to the previous one. 122 | 123 | [dbg-stmt]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger 124 | 125 | [![Screenshot of debugging the Game of Life](../images/game-of-life/debugging.png)](../images/game-of-life/debugging.png) 126 | 127 | ## Exercises 128 | 129 | * Add logging to the `tick` function that records the row and column of each 130 | cell that transitioned states from live to dead or vice versa. 131 | 132 | * Introduce a `panic!()` in the `Universe::new` method. Inspect the panic's 133 | backtrace in your Web browser's JavaScript debugger. Disable debug symbols, 134 | rebuild without the `console_error_panic_hook` optional dependency, and 135 | inspect the stack trace again. Not as useful is it? 136 | -------------------------------------------------------------------------------- /src/game-of-life/deploying-to-production.md: -------------------------------------------------------------------------------- 1 | # Deploying our Game of Life Web Application to Production 2 | 3 | When we're happy with the project, the next step is to deploy it to a production 4 | server instead of our local development server. This is generally the same with 5 | Rust and WebAssembly as it is with anything else: put the files somewhere where 6 | they are exposed to the Web via an HTTP server! 7 | 8 | We make sure our wasm module is an up-to-date build by running `wasm-pack` from 9 | within the `wasm-game-of-life` directory: 10 | 11 | ``` 12 | wasm-pack build 13 | ``` 14 | 15 | And then we bundle our JavaScript and and HTML by running `webpack` within the 16 | `wasm-game-of-life/www` directory: 17 | 18 | ``` 19 | ./node_modules/.bin/webpack 20 | ``` 21 | 22 | This bundles the whole Web application—the `.wasm` binary, JavaScript files, and 23 | our HTML—and outputs it into the `wasm-game-of-life/www/dist` directory. Its 24 | contents should looks something like this: 25 | 26 | ``` 27 | wasm-game-of-life/www/dist/ 28 | ├── 0.bootstrap.js 29 | ├── 357c6c6c57e15cecdc07.module.wasm 30 | ├── bootstrap.js 31 | └── index.html 32 | ``` 33 | 34 | To use our application from a server, it must be properly configured to serve 35 | `.wasm` files with the correct [MIME type][MIME]—`application/wasm`. 36 | 37 | [MIME]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types 38 | 39 | > **Note**: Server configuration varies by operating system, it's recommended 40 | > you look up a tutorial for your specific operating system and webserver. These 41 | > examples assume an environment like Debian/Ubuntu or CentOS/Red Hat/Fedora. 42 | 43 | For example with nginx, [add `application/wasm wasm;` to `/etc/nginx/mime.types` 44 | ][nginx-mime]. Then reload nginx with `sudo nginx -s reload` to pick up the 45 | configuration change. 46 | 47 | [nginx-mime]: https://nginx.org/en/docs/http/ngx_http_core_module.html#types 48 | 49 | For Apache [add `AddType application/wasm .wasm` to the root of your apache 50 | config][apache-mime], likely located at either `/etc/apache2/apache2.conf` or 51 | `/etc/httpd.d/conf/httpd.conf`. Reload Apache with `sudo apachectl -k graceful`. 52 | 53 | [apache-mime]: https://httpd.apache.org/docs/2.4/mod/mod_mime.html#addtype 54 | 55 | Finally, we can upload the contents of the `dist` directory to the production 56 | server, for example using SCP or an SFTP client. Copying the files to the web 57 | root (usually `/var/www/html` or `/var/www`) will allow anybody to see the final 58 | product. 59 | -------------------------------------------------------------------------------- /src/game-of-life/hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello, World! 2 | 3 | This section will show you how to build and run your first Rust and WebAssembly 4 | program: a Web page that alerts "Hello, World!" 5 | 6 | Make sure you have followed the [setup instructions](setup.html) before beginning. 7 | 8 | ## Clone the Project Template 9 | 10 | The project template comes pre-configured with sane defaults, so you can quickly 11 | build, integrate, and package your code for the Web. 12 | 13 | Clone the project template with this command: 14 | 15 | ```text 16 | cargo generate --git https://github.com/rustwasm/wasm-pack-template 17 | ``` 18 | 19 | This should prompt you for the new project's name. We will use 20 | **"wasm-game-of-life"**. 21 | 22 | ```text 23 | wasm-game-of-life 24 | ``` 25 | 26 | ## What's Inside 27 | 28 | Enter the new `wasm-game-of-life` project 29 | 30 | ``` 31 | cd wasm-game-of-life 32 | ``` 33 | 34 | and let's take a look at its contents: 35 | 36 | ```text 37 | wasm-game-of-life/ 38 | ├── Cargo.toml 39 | ├── LICENSE_APACHE 40 | ├── LICENSE_MIT 41 | ├── README.md 42 | └── src 43 | ├── lib.rs 44 | └── utils.rs 45 | ``` 46 | 47 | Let's take a look at a couple of these files in detail. 48 | 49 | ### `wasm-game-of-life/Cargo.toml` 50 | 51 | The `Cargo.toml` file specifies dependencies and metadata for `cargo`, Rust's 52 | package manager and build tool. This one comes pre-configured with a 53 | `wasm-bindgen` dependency, a few optional dependencies we will dig into later, 54 | and the `crate-type` properly initialized for generating `.wasm` libraries. 55 | 56 | ### `wasm-game-of-life/src/lib.rs` 57 | 58 | The `src/lib.rs` file is the root of the Rust crate that we are compiling to 59 | WebAssembly. It uses `wasm-bindgen` to interface with JavaScript. It imports the 60 | `window.alert` JavaScript function, and exports the `greet` Rust function, which 61 | alerts a greeting message. 62 | 63 | ```rust 64 | extern crate cfg_if; 65 | extern crate wasm_bindgen; 66 | 67 | mod utils; 68 | 69 | use cfg_if::cfg_if; 70 | use wasm_bindgen::prelude::*; 71 | 72 | cfg_if! { 73 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global 74 | // allocator. 75 | if #[cfg(feature = "wee_alloc")] { 76 | extern crate wee_alloc; 77 | #[global_allocator] 78 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 79 | } 80 | } 81 | 82 | #[wasm_bindgen] 83 | extern { 84 | fn alert(s: &str); 85 | } 86 | 87 | #[wasm_bindgen] 88 | pub fn greet() { 89 | alert("Hello, wasm-game-of-life!"); 90 | } 91 | 92 | ``` 93 | 94 | ### `wasm-game-of-life/src/utils.rs` 95 | 96 | The `src/utils.rs` module provides common utilities to make working with Rust 97 | compiled to WebAssembly easier. We will take a look at some of these utilities 98 | in more detail later in the tutorial, such as when we look at [debugging our wasm 99 | code](debugging.html), but we can ignore this file for now. 100 | 101 | ## Build the Project 102 | 103 | We use `wasm-pack` to orchestrate the following build steps: 104 | 105 | * Ensure that we have Rust 1.30 or newer and the `wasm32-unknown-unknown` 106 | target installed via `rustup`, 107 | * Compile our Rust sources into a WebAssembly `.wasm` binary via `cargo`, 108 | * Use `wasm-bindgen` to generate the JavaScript API for using our Rust-generated 109 | WebAssembly. 110 | 111 | To do all of that, run this command inside the project directory: 112 | 113 | ``` 114 | wasm-pack build 115 | ``` 116 | 117 | When the build has completed, we can find its artifacts in the `pkg` directory, 118 | and it should have these contents: 119 | 120 | ``` 121 | pkg/ 122 | ├── package.json 123 | ├── README.md 124 | ├── wasm_game_of_life_bg.wasm 125 | ├── wasm_game_of_life.d.ts 126 | └── wasm_game_of_life.js 127 | ``` 128 | 129 | The `README.md` file is copied from the main project, but the others are 130 | completely new. 131 | 132 | ### `wasm-game-of-life/pkg/wasm_game_of_life_bg.wasm` 133 | 134 | The `.wasm` file is the WebAssembly binary that is generated by the Rust 135 | compiler from our Rust sources. It contains the compiled-to-wasm versions of all 136 | of our Rust functions and data. For example, it has an exported "greet" 137 | function. 138 | 139 | ### `wasm-game-of-life/pkg/wasm_game_of_life.js` 140 | 141 | The `.js` file is generated by `wasm-bindgen` and contains JavaScript glue for 142 | importing DOM and JavaScript functions into Rust and exposing a nice API to the 143 | WebAssembly functions to JavaScript. For example, there is a JavaScript `greet` 144 | function that wraps the `greet` function exported from the WebAssembly 145 | module. Right now, this glue isn't doing much, but when we start passing more 146 | interesting values back and forth between wasm and JavaScript, it will help 147 | shepherd those values across the boundary. 148 | 149 | ```js 150 | import * as wasm from './wasm_game_of_life_bg'; 151 | 152 | // ... 153 | 154 | export function greet() { 155 | return wasm.greet(); 156 | } 157 | ``` 158 | 159 | ### `wasm-game-of-life/pkg/wasm_game_of_life.d.ts` 160 | 161 | The `.d.ts` file contains [TypeScript][] type declarations for the JavaScript 162 | glue. If you are using TypeScript, you can have your calls into WebAssembly 163 | functions type checked, and your IDE can provide autocompletions and 164 | suggestions! If you aren't using TypeScript, you can safely ignore this file. 165 | 166 | ```typescript 167 | export function greet(): void; 168 | ``` 169 | 170 | [TypeScript]: http://www.typescriptlang.org/ 171 | 172 | ### `wasm-game-of-life/pkg/package.json` 173 | 174 | [The `package.json` file contains metadata about the generated JavaScript and 175 | WebAssembly package.][package.json] This is used by npm and JavaScript bundlers 176 | to determine dependencies across packages, package names, versions, and a bunch 177 | of other stuff. It helps us integrate with JavaScript tooling and allows us to 178 | publish our package to npm. 179 | 180 | ```json 181 | { 182 | "name": "wasm-game-of-life", 183 | "collaborators": [ 184 | "Your Name " 185 | ], 186 | "description": null, 187 | "version": "0.1.0", 188 | "license": null, 189 | "repository": null, 190 | "files": [ 191 | "wasm_game_of_life_bg.wasm", 192 | "wasm_game_of_life.d.ts" 193 | ], 194 | "main": "wasm_game_of_life.js", 195 | "types": "wasm_game_of_life.d.ts" 196 | } 197 | ``` 198 | 199 | [package.json]: https://docs.npmjs.com/files/package.json 200 | 201 | ## Putting it into a Web Page 202 | 203 | To take our `wasm-game-of-life` package and use it in a Web page, we use [the 204 | `create-wasm-app` JavaScript project template][create-wasm-app]. 205 | 206 | [create-wasm-app]: https://github.com/rustwasm/create-wasm-app 207 | 208 | Run this command within the `wasm-game-of-life` directory: 209 | 210 | ``` 211 | npm init wasm-app www 212 | ``` 213 | 214 | Here's what our new `wasm-game-of-life/www` subdirectory contains: 215 | 216 | ``` 217 | wasm-game-of-life/www/ 218 | ├── bootstrap.js 219 | ├── index.html 220 | ├── index.js 221 | ├── LICENSE-APACHE 222 | ├── LICENSE-MIT 223 | ├── package.json 224 | ├── README.md 225 | └── webpack.config.js 226 | ``` 227 | 228 | Once again, let's take a closer look at some of these files. 229 | 230 | ### `wasm-game-of-life/www/package.json` 231 | 232 | This `package.json` comes pre-configured with `webpack` and `webpack-dev-server` 233 | dependencies, as well as a dependency on `hello-wasm-pack`, which is a version 234 | of the initial `wasm-pack-template` package that has been published to npm. 235 | 236 | ### `wasm-game-of-life/www/webpack.config.js` 237 | 238 | This file configures webpack and its local development server. It comes 239 | pre-configured, and you shouldn't have to tweak this at all to get webpack and 240 | its local development server working. 241 | 242 | ### `wasm-game-of-life/www/index.html` 243 | 244 | This is the root HTML file for the Web page. It doesn't do much other than 245 | load `bootstrap.js`, which is a very thin wrapper around `index.js`. 246 | 247 | ```html 248 | 249 | 250 | 251 | 252 | Hello wasm-pack! 253 | 254 | 255 | 256 | 257 | 258 | ``` 259 | 260 | ### `wasm-game-of-life/www/index.js` 261 | 262 | The `index.js` is the main entry point for our Web page's JavaScript. It imports 263 | the `hello-wasm-pack` npm package, which contains the default 264 | `wasm-pack-template`'s compiled WebAssembly and JavaScript glue, then it calls 265 | `hello-wasm-pack`'s `greet` function. 266 | 267 | ```js 268 | import * as wasm from "hello-wasm-pack"; 269 | 270 | wasm.greet(); 271 | ``` 272 | 273 | ### Install the dependencies 274 | 275 | First, ensure that the local development server and its dependencies are 276 | installed by running `npm install` within the `wasm-game-of-life/www` 277 | subdirectory: 278 | 279 | ```text 280 | npm install 281 | ``` 282 | 283 | This command only needs to be run once, and will install the `webpack` 284 | JavaScript bundler and its development server. 285 | 286 | > Note that `webpack` is not required for working with Rust and WebAssembly, it 287 | > is just the bundler and development server we've chosen for convenience 288 | > here. Parcel and Rollup should also support importing WebAssembly as 289 | > ECMAScript modules. 290 | 291 | ### Using our Local `wasm-game-of-life` Package in `www` 292 | 293 | Rather than use the `hello-wasm-pack` package from npm, we want to use our local 294 | `wasm-game-of-life` package instead. This will allow us to incrementally develop 295 | our Game of Life program. 296 | 297 | First, run `npm link` inside the `wasm-game-of-life/pkg` directory, so that the 298 | local package can be depended upon by other local packages without publishing 299 | them to npm: 300 | 301 | ```bash 302 | npm link 303 | ``` 304 | 305 | > 🐞 Did you get `EACCESS` or permissions errors when running `npm link`? [How 306 | > to Prevent Permissions Errors with 307 | > `npm`.](https://docs.npmjs.com/getting-started/fixing-npm-permissions) 308 | 309 | Second, use the `npm link`ed version of the `wasm-game-of-life` from the `www` 310 | package by running this command within `wasm-game-of-life/www`: 311 | 312 | ``` 313 | npm link wasm-game-of-life 314 | ``` 315 | 316 | Finally, modify `wasm-game-of-life/www/index.js` to import `wasm-game-of-life` 317 | instead of the `hello-wasm-pack` package: 318 | 319 | ```js 320 | import * as wasm from "wasm-game-of-life"; 321 | 322 | wasm.greet(); 323 | ``` 324 | 325 | Our Web page is now ready to be served locally! 326 | 327 | ## Serving Locally 328 | 329 | Next, open a new terminal for the development server. Running the server in a 330 | new terminal lets us leave it running in the background, and doesn't block us 331 | from running other commands in the meantime. In the new terminal, run this 332 | command from within the `wasm-game-of-life/www` directory: 333 | 334 | ``` 335 | npm run start 336 | ``` 337 | 338 | Navigate your Web browser to [http://localhost:8080/](http://localhost:8080/) 339 | and you should be greeted with an alert message: 340 | 341 | [![Screenshot of the "Hello, wasm-game-of-life!" Web page alert](../images/game-of-life/hello-world.png)](../images/game-of-life/hello-world.png) 342 | 343 | Anytime you make changes and want them reflected on 344 | [http://localhost:8080/](http://localhost:8080/), just re-run the `wasm-pack 345 | build` command within the `wasm-game-of-life` directory. 346 | 347 | ## Exercises 348 | 349 | * Modify the `greet` function in `wasm-game-of-life/src/lib.rs` to take a `name: 350 | &str` parameter that customizes the alerted message, and pass your name to the 351 | `greet` function from inside `wasm-game-of-life/www/index.js`. Rebuild the 352 | `.wasm` binary with `wasm-pack build`, then refresh 353 | [http://localhost:8080/](http://localhost:8080/) in your Web browser and you 354 | should see a customized greeting! 355 | 356 |
357 | Answer 358 | 359 | New version of the `greet` function in `wasm-game-of-life/src/lib.rs`: 360 | 361 | ```rust 362 | #[wasm_bindgen] 363 | pub fn greet(name: &str) { 364 | alert(&format!("Hello, {}!", name)); 365 | } 366 | ``` 367 | 368 | New invocation of `greet` in `wasm-game-of-life/www/index.js`: 369 | 370 | ```js 371 | wasm.greet("Your Name"); 372 | ``` 373 | 374 |
375 | -------------------------------------------------------------------------------- /src/game-of-life/implementing.md: -------------------------------------------------------------------------------- 1 | # Implementing Conway's Game of Life 2 | 3 | ## Design 4 | 5 | Before we dive in, we have some design choices to consider. 6 | 7 | ### Infinite Universe 8 | 9 | The Game of Life is played in an infinite universe, but we do not have infinite 10 | memory and compute power. Working around this rather annoying limitation usually 11 | comes in one of three flavors: 12 | 13 | 1. Keep track of which subset of the universe has interesting things happening, 14 | and expand this region as needed. In the worst case, this expansion is 15 | unbounded and the implementation will get slower and slower and eventually 16 | run out of memory. 17 | 18 | 2. Create a fixed-size universe, where cells on the edges have fewer neighbors 19 | than cells in the middle. The downside with this approach is that infinite 20 | patterns, like gliders, that reach the end of the universe are snuffed out. 21 | 22 | 3. Create a fixed-size, periodic universe, where cells on the edges have 23 | neighbors that wrap around to the other side of the universe. Because 24 | neighbors wrap around the edges of the universe, gliders can keep running 25 | forever. 26 | 27 | We will implement the third option. 28 | 29 | ### Interfacing Rust and JavaScript 30 | 31 | > ⚡ This is one of the most important concepts to understand and take away from 32 | > this tutorial! 33 | 34 | JavaScript's garbage-collected heap — where `Object`s, `Array`s, and DOM nodes 35 | are allocated — is distinct from WebAssembly's linear memory space, where our 36 | Rust values live. WebAssembly currently has no direct access to the 37 | garbage-collected heap (as of April 2018, this is expected to change with the 38 | ["host bindings" proposal][host-bindings]). JavaScript, on the other hand, can 39 | read and write to the WebAssembly linear memory space, but only as an 40 | [`ArrayBuffer`][array-buf] of scalar values (`u8`, `i32`, `f64`, 41 | etc...). WebAssembly functions also take and return scalar values. These are the 42 | building blocks from which all WebAssembly and JavaScript communication is 43 | constituted. 44 | 45 | [host-bindings]: https://github.com/WebAssembly/host-bindings/blob/master/proposals/host-bindings/Overview.md 46 | [array-buf]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer 47 | 48 | `wasm_bindgen` defines a common understanding of how to work with compound 49 | structures across this boundary. It involves boxing Rust structures, and 50 | wrapping the pointer in a JavaScript class for usability, or indexing into a 51 | table of JavaScript objects from Rust. `wasm_bindgen` is very convenient, but it 52 | does not remove the need to consider our data representation, and what values 53 | and structures are passed across this boundary. Instead, think of it as a tool 54 | for implementing the interface design you choose. 55 | 56 | When designing an interface between WebAssembly and JavaScript, we want to 57 | optimize for the following properties: 58 | 59 | 1. **Minimizing copying into and out of the WebAssembly linear memory.** 60 | Unnecessary copies impose unnecessary overhead. 61 | 62 | 2. **Minimizing serializing and deserializing.** Similar to copies, serializing 63 | and deserializing also imposes overhead, and often imposes copying as 64 | well. If we can pass opaque handles to a data structure — instead of 65 | serializing it on one side, copying it into some known location in the 66 | WebAssembly linear memory, and deserializing on the other side — we can often 67 | reduce a lot of overhead. `wasm_bindgen` helps us define and work with opaque 68 | handles to JavaScript `Object`s or boxed Rust structures. 69 | 70 | As a general rule of thumb, a good JavaScript↔WebAssembly interface design is 71 | often one where large, long-lived data structures are implemented as Rust types 72 | that live in the WebAssembly linear memory, and are exposed to JavaScript as 73 | opaque handles. JavaScript calls exported WebAssembly functions that take these 74 | opaque handles, transform their data, perform heavy computations, query the 75 | data, and ultimately return a small, copy-able result. By only returning the 76 | small result of the computation, we avoid copying and/or serializing everything 77 | back and forth between the JavaScript garbage-collected heap and the WebAssembly 78 | linear memory. 79 | 80 | ### Interfacing Rust and JavaScript in our Game of Life 81 | 82 | Let's start by enumerating some hazards to avoid. We don't want to copy the 83 | whole universe into and out of the WebAssembly linear memory on every tick. We 84 | do not want to allocate objects for every cell in the universe, nor do we want 85 | to impose a cross-boundary call to read and write each cell. 86 | 87 | Where does this leave us? We can represent the universe as a flat array that 88 | lives in the WebAssembly linear memory, and has a byte for each cell. `0` is a 89 | dead cell and `1` is a live cell. 90 | 91 | Here is what a 4 by 4 universe looks like in memory: 92 | 93 | ![Screenshot of a 4 by 4 universe](../images/game-of-life/universe.png) 94 | 95 | To find the array index of the cell at a given row and column in the universe, 96 | we can use this formula: 97 | 98 | ```text 99 | index(row, column, universe) = row * width(universe) + column 100 | ``` 101 | 102 | We have several ways of exposing the universe's cells to JavaScript. To begin, 103 | we will implement [`std::fmt::Display`][`Display`] for `Universe`, which we can 104 | use to generate a Rust `String` of the cells rendered as text characters. This 105 | Rust String is then copied from the WebAssembly linear memory into a JavaScript 106 | String in the JavaScript's garbage-collected heap, and is then displayed by 107 | setting HTML `textContent`. Later in the chapter, we'll evolve this 108 | implementation to avoid copying the universe's cells between heaps and to render 109 | to ``. 110 | 111 | *Another viable design alternative would be for Rust to return a list of every 112 | cell that changed states after each tick, instead of exposing the whole universe 113 | to JavaScript. This way, JavaScript wouldn't need to iterate over the whole 114 | universe when rendering, only the relevant subset. The trade off is that this 115 | delta-based design is slightly more difficult to implement.* 116 | 117 | ## Rust Implementation 118 | 119 | In the last chapter, we cloned an initial project template. We will modify that 120 | project template now. 121 | 122 | Let's begin by removing the `alert` import and `greet` function from 123 | `wasm-game-of-life/src/lib.rs`, and replacing them with a type definition for 124 | cells: 125 | 126 | ```rust 127 | #[wasm_bindgen] 128 | #[repr(u8)] 129 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 130 | pub enum Cell { 131 | Dead = 0, 132 | Alive = 1, 133 | } 134 | ``` 135 | 136 | It is important that we have `#[repr(u8)]`, so that each cell is represented as 137 | a single byte. It is also important that the `Dead` variant is `0` and that the 138 | `Alive` variant is `1`, so that we can easily count a cell's live neighbors with 139 | addition. 140 | 141 | Next, let's define the universe. The universe has a width and a height, and a 142 | vector of cells of length `width * height`. 143 | 144 | ```rust 145 | #[wasm_bindgen] 146 | pub struct Universe { 147 | width: u32, 148 | height: u32, 149 | cells: Vec, 150 | } 151 | ``` 152 | 153 | To access the cell at a given row and column, we translate the row and column 154 | into an index into the cells vector, as described earlier: 155 | 156 | ```rust 157 | impl Universe { 158 | fn get_index(&self, row: u32, column: u32) -> usize { 159 | (row * self.width + column) as usize 160 | } 161 | 162 | // ... 163 | } 164 | ``` 165 | 166 | In order to calculate the next state of a cell, we need to get a count of how 167 | many of its neighbors are alive. Let's write a `live_neighbor_count` method to 168 | do just that! 169 | 170 | ```rust 171 | impl Universe { 172 | // ... 173 | 174 | fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { 175 | let mut count = 0; 176 | for delta_row in [self.height - 1, 0, 1].iter().cloned() { 177 | for delta_col in [self.width - 1, 0, 1].iter().cloned() { 178 | if delta_row == 0 && delta_col == 0 { 179 | continue; 180 | } 181 | 182 | let neighbor_row = (row + delta_row) % self.height; 183 | let neighbor_col = (column + delta_col) % self.width; 184 | let idx = self.get_index(neighbor_row, neighbor_col); 185 | count += self.cells[idx] as u8; 186 | } 187 | } 188 | count 189 | } 190 | } 191 | ``` 192 | 193 | The `live_neighbor_count` method uses deltas and modulo to avoid special casing 194 | the edges of the universe with `if`s. When applying a delta of `-1`, we *add* 195 | `self.height - 1` and let the modulo do its thing, rather than attempting to 196 | subtract `1`. `row` and `column` can be `0`, and if we attempted to subtract `1` 197 | from them, there would be an unsigned integer underflow. 198 | 199 | Now we have everything we need to compute the next generation from the current 200 | one! Each of the Game's rules follows a straightforward translation into a 201 | condition on a `match` expression. Additionally, because we want JavaScript to 202 | control when ticks happen, we will put this method inside a `#[wasm_bindgen]` 203 | block, so that it gets exposed to JavaScript. 204 | 205 | ```rust 206 | /// Public methods, exported to JavaScript. 207 | #[wasm_bindgen] 208 | impl Universe { 209 | pub fn tick(&mut self) { 210 | let mut next = self.cells.clone(); 211 | 212 | for row in 0..self.height { 213 | for col in 0..self.width { 214 | let idx = self.get_index(row, col); 215 | let cell = self.cells[idx]; 216 | let live_neighbors = self.live_neighbor_count(row, col); 217 | 218 | let next_cell = match (cell, live_neighbors) { 219 | // Rule 1: Any live cell with fewer than two live neighbours 220 | // dies, as if caused by underpopulation. 221 | (Cell::Alive, x) if x < 2 => Cell::Dead, 222 | // Rule 2: Any live cell with two or three live neighbours 223 | // lives on to the next generation. 224 | (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, 225 | // Rule 3: Any live cell with more than three live 226 | // neighbours dies, as if by overpopulation. 227 | (Cell::Alive, x) if x > 3 => Cell::Dead, 228 | // Rule 4: Any dead cell with exactly three live neighbours 229 | // becomes a live cell, as if by reproduction. 230 | (Cell::Dead, 3) => Cell::Alive, 231 | // All other cells remain in the same state. 232 | (otherwise, _) => otherwise, 233 | }; 234 | 235 | next[idx] = next_cell; 236 | } 237 | } 238 | 239 | self.cells = next; 240 | } 241 | 242 | // ... 243 | } 244 | ``` 245 | 246 | So far, the state of the universe is represented as a vector of cells. To make 247 | this human readable, let's implement a basic text renderer. The idea is to write 248 | the universe line by line as text, and for each cell that is alive, print the 249 | Unicode character `◼` ("black medium square"). For dead cells, we'll print `◻` 250 | (a "white medium square"). 251 | 252 | By implementing the [`Display`] trait from Rust's standard library, we can add a 253 | way to format a structure in a user-facing manner. This will also automatically 254 | give us a [`to_string`] method. 255 | 256 | [`Display`]: https://doc.rust-lang.org/1.25.0/std/fmt/trait.Display.html 257 | [`to_string`]: https://doc.rust-lang.org/1.25.0/std/string/trait.ToString.html 258 | 259 | ```rust 260 | use std::fmt; 261 | 262 | impl fmt::Display for Universe { 263 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 264 | for line in self.cells.as_slice().chunks(self.width as usize) { 265 | for &cell in line { 266 | let symbol = if cell == Cell::Dead { '◻' } else { '◼' }; 267 | write!(f, "{}", symbol)?; 268 | } 269 | write!(f, "\n")?; 270 | } 271 | 272 | Ok(()) 273 | } 274 | } 275 | ``` 276 | 277 | Finally, we define a constructor that initializes the universe with an 278 | interesting pattern of live and dead cells, as well as a `render` method: 279 | 280 | ```rust 281 | /// Public methods, exported to JavaScript. 282 | #[wasm_bindgen] 283 | impl Universe { 284 | // ... 285 | 286 | pub fn new() -> Universe { 287 | let width = 64; 288 | let height = 64; 289 | 290 | let cells = (0..width * height) 291 | .map(|i| { 292 | if i % 2 == 0 || i % 7 == 0 { 293 | Cell::Alive 294 | } else { 295 | Cell::Dead 296 | } 297 | }) 298 | .collect(); 299 | 300 | Universe { 301 | width, 302 | height, 303 | cells, 304 | } 305 | } 306 | 307 | pub fn render(&self) -> String { 308 | self.to_string() 309 | } 310 | } 311 | ``` 312 | 313 | With that, the Rust half of our Game of Life implementation is complete! 314 | 315 | Recompile it to WebAssembly by running `wasm-pack build` within the 316 | `wasm-game-of-life` directory. 317 | 318 | ## Rendering with JavaScript 319 | 320 | First, let's add a `
` element to `wasm-game-of-life/www/index.html` to
321 | render the universe into, just above the `
327 | 
328 | ```
329 | 
330 | Additionally, we want the `
` centered in the middle of the Web page. We can
331 | use CSS flex boxes to accomplish this task. Add the following `
348 | ```
349 | 
350 | At the top of `wasm-game-of-life/www/index.js`, let's fix our import to bring in
351 | the `Universe` rather than the old `greet` function:
352 | 
353 | ```js
354 | import { Universe } from "wasm-game-of-life";
355 | ```
356 | 
357 | Also, let's get that `
` element we just added and instantiate a new
358 | universe:
359 | 
360 | ```js
361 | const pre = document.getElementById("game-of-life-canvas");
362 | const universe = Universe.new();
363 | ```
364 | 
365 | The JavaScript runs in [a `requestAnimationFrame`
366 | loop][requestAnimationFrame]. On each iteration, it draws the current universe
367 | to the `
`, and then calls `Universe::tick`.
368 | 
369 | [requestAnimationFrame]: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
370 | 
371 | ```js
372 | const renderLoop = () => {
373 |   pre.textContent = universe.render();
374 |   universe.tick();
375 | 
376 |   requestAnimationFrame(renderLoop);
377 | };
378 | ```
379 | 
380 | To start the rendering process, all we have to do is make the initial call for
381 | the first iteration of the rendering loop:
382 | 
383 | ```js
384 | requestAnimationFrame(renderLoop);
385 | ```
386 | 
387 | Make sure your development server is still running (run `npm run start` inside
388 | `wasm-game-of-life/www`) and this is what
389 | [http://localhost:8080/](http://localhost:8080/) should look like:
390 | 
391 | [![Screenshot of the Game of Life implementation with text rendering](../images/game-of-life/initial-game-of-life-pre.png)](../images/game-of-life/initial-game-of-life-pre.png)
392 | 
393 | ## Rendering to Canvas Directly from Memory
394 | 
395 | Generating (and allocating) a `String` in Rust and then having `wasm-bindgen`
396 | convert it to a valid JavaScript string makes unnecessary copies of the
397 | universe's cells. As the JavaScript code already knows the width and
398 | height of the universe, and can read WebAssembly's linear memory that make up
399 | the cells directly, we'll modify the `render` method to return a pointer to the
400 | start of the cells array.
401 | 
402 | Also, instead of rendering Unicode text, we'll switch to using the [Canvas
403 | API]. We will use this design in the rest of the tutorial.
404 | 
405 | [Canvas API]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
406 | 
407 | Inside `wasm-game-of-life/www/index.html`, let's replace the `
` we added
408 | earlier with a `` we will render into (it too should be within the
409 | ``, before the `
415 | 
416 | ```
417 | 
418 | To get the necessary information from the Rust implementation, we'll need to add
419 | some more getter functions for a universe's width, height, and pointer to its
420 | cells array. All of these are exposed to JavaScript as well. Make these
421 | additions to `wasm-game-of-life/src/lib.rs`:
422 | 
423 | ```rust
424 | /// Public methods, exported to JavaScript.
425 | #[wasm_bindgen]
426 | impl Universe {
427 |     // ...
428 | 
429 |     pub fn width(&self) -> u32 {
430 |         self.width
431 |     }
432 | 
433 |     pub fn height(&self) -> u32 {
434 |         self.height
435 |     }
436 | 
437 |     pub fn cells(&self) -> *const Cell {
438 |         self.cells.as_ptr()
439 |     }
440 | }
441 | ```
442 | 
443 | Next, in `wasm-game-of-life/www/index.js`, let's also import `Cell` from
444 | `wasm-game-of-life`, and define some constants that we will use when rendering
445 | to the canvas:
446 | 
447 | ```js
448 | import { Universe, Cell } from "wasm-game-of-life";
449 | 
450 | const CELL_SIZE = 5; // px
451 | const GRID_COLOR = "#CCCCCC";
452 | const DEAD_COLOR = "#FFFFFF";
453 | const ALIVE_COLOR = "#000000";
454 | ```
455 | 
456 | Now, let's rewrite the rest of this JavaScript code to no longer write to the
457 | `
`'s `textContent` but instead draw to the ``:
458 | 
459 | ```js
460 | // Construct the universe, and get its width and height.
461 | const universe = Universe.new();
462 | const width = universe.width();
463 | const height = universe.height();
464 | 
465 | // Give the canvas room for all of our cells and a 1px border
466 | // around each of them.
467 | const canvas = document.getElementById("game-of-life-canvas");
468 | canvas.height = (CELL_SIZE + 1) * height + 1;
469 | canvas.width = (CELL_SIZE + 1) * width + 1;
470 | 
471 | const ctx = canvas.getContext('2d');
472 | 
473 | const renderLoop = () => {
474 |   universe.tick();
475 | 
476 |   drawGrid();
477 |   drawCells();
478 | 
479 |   requestAnimationFrame(renderLoop);
480 | };
481 | ```
482 | 
483 | To draw the grid between cells, we draw a set of equally-spaced horizontal
484 | lines, and a set of equally-spaced vertical lines. These lines criss-cross to
485 | form the grid.
486 | 
487 | ```js
488 | const drawGrid = () => {
489 |   ctx.beginPath();
490 |   ctx.strokeStyle = GRID_COLOR;
491 | 
492 |   // Vertical lines.
493 |   for (let i = 0; i <= width; i++) {
494 |     ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0);
495 |     ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1);
496 |   }
497 | 
498 |   // Horizontal lines.
499 |   for (let j = 0; j <= height; j++) {
500 |     ctx.moveTo(0,                           j * (CELL_SIZE + 1) + 1);
501 |     ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1);
502 |   }
503 | 
504 |   ctx.stroke();
505 | };
506 | ```
507 | 
508 | We can directly access WebAssembly's linear memory via `memory`, which is
509 | defined in the raw wasm module `wasm_game_of_life_bg`. To draw the cells, we
510 | get a pointer to the universe's cells, construct a `Uint8Array` overlaying the
511 | cells buffer, iterate over each cell, and draw a white or black rectangle
512 | depending on whether the cell is dead or alive, respectively. By working with
513 | pointers and overlays, we avoid copying the cells across the boundary on every
514 | tick.
515 | 
516 | ```js
517 | // Import the WebAssembly memory at the top of the file.
518 | import { memory } from "wasm-game-of-life/wasm_game_of_life_bg";
519 | 
520 | // ...
521 | 
522 | const getIndex = (row, column) => {
523 |   return row * width + column;
524 | };
525 | 
526 | const drawCells = () => {
527 |   const cellsPtr = universe.cells();
528 |   const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);
529 | 
530 |   ctx.beginPath();
531 | 
532 |   for (let row = 0; row < height; row++) {
533 |     for (let col = 0; col < width; col++) {
534 |       const idx = getIndex(row, col);
535 | 
536 |       ctx.fillStyle = cells[idx] === Cell.Dead
537 |         ? DEAD_COLOR
538 |         : ALIVE_COLOR;
539 | 
540 |       ctx.fillRect(
541 |         col * (CELL_SIZE + 1) + 1,
542 |         row * (CELL_SIZE + 1) + 1,
543 |         CELL_SIZE,
544 |         CELL_SIZE
545 |       );
546 |     }
547 |   }
548 | 
549 |   ctx.stroke();
550 | };
551 | ```
552 | 
553 | To start the rendering process, we'll use the same code as above to start the
554 | first iteration of the rendering loop:
555 | 
556 | ```js
557 | drawGrid();
558 | drawCells();
559 | requestAnimationFrame(renderLoop);
560 | ```
561 | 
562 | Note that we call `drawGrid()` and `drawCells()` here _before_ we call
563 | `requestAnimationFrame()`. The reason we do this is so that the _initial_ state
564 | of the universe is drawn before we make modifications. If we instead simply
565 | called `requestAnimationFrame(renderLoop)`, we'd end up with a situation where
566 | the first frame that was drawn would actually be _after_ the first call to
567 | `universe.tick()`, which is the second "tick" of the life of these cells.
568 | 
569 | ## It Works!
570 | 
571 | Rebuild the WebAssembly and bindings glue by running this command from within
572 | the root `wasm-game-of-life` directory:
573 | 
574 | ```
575 | wasm-pack build
576 | ```
577 | 
578 | Make sure your development server is still running. If it isn't, start it again
579 | from within the `wasm-game-of-life/www` directory:
580 | 
581 | ```
582 | npm run start
583 | ```
584 | 
585 | If you refresh [http://localhost:8080/](http://localhost:8080/), you should be
586 | greeted with an exciting display of life!
587 | 
588 | [![Screenshot of the Game of Life implementation](../images/game-of-life/initial-game-of-life.png)](../images/game-of-life/initial-game-of-life.png)
589 | 
590 | As an aside, there is also a really neat algorithm for implementing the Game of
591 | Life called [hashlife](https://en.wikipedia.org/wiki/Hashlife). It uses
592 | aggressive memoizing and can actually get *exponentially faster* to compute
593 | future generations the longer it runs! Given that, you might be wondering why we
594 | didn't implement hashlife in this tutorial. It is out of scope for this text,
595 | where we are focusing on Rust and WebAssembly integration, but we highly
596 | encourage you to go learn about hashlife on your own!
597 | 
598 | ## Exercises
599 | 
600 | * Initialize the universe with a single space ship.
601 | 
602 | * Instead of hard-coding the initial universe, generate a random one, where each
603 |   cell has a fifty-fifty chance of being alive or dead.
604 | 
605 |   *Hint: use [the `js-sys` crate](https://crates.io/crates/js-sys) to import
606 |   [the `Math.random` JavaScript
607 |   function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random).*
608 | 
609 |   
610 | Answer 611 | *First, add `js-sys` as a dependency in `wasm-game-of-life/Cargo.toml`:* 612 | 613 | ```toml 614 | # ... 615 | [dependencies] 616 | js-sys = "0.3" 617 | # ... 618 | ``` 619 | 620 | *Then, use the `js_sys::Math::random` function to flip a coin:* 621 | 622 | ```rust 623 | extern crate js_sys; 624 | 625 | // ... 626 | 627 | if js_sys::Math::random() < 0.5 { 628 | // Alive... 629 | } else { 630 | // Dead... 631 | } 632 | ``` 633 |
634 | 635 | * Representing each cell with a byte makes iterating over cells easy, but it 636 | comes at the cost of wasting memory. Each byte is eight bits, but we only 637 | require a single bit to represent whether each cell is alive or dead. Refactor 638 | the data representation so that each cell uses only a single bit of space. 639 | 640 |
641 | Answer 642 | 643 | In Rust, you can use [the `fixedbitset` crate and its `FixedBitSet` 644 | type](https://crates.io/crates/fixedbitset) to represent cells instead of 645 | `Vec`: 646 | 647 | ```rust 648 | // Make sure you also added the dependency to Cargo.toml! 649 | extern crate fixedbitset; 650 | use fixedbitset::FixedBitSet; 651 | 652 | // ... 653 | 654 | #[wasm_bindgen] 655 | pub struct Universe { 656 | width: u32, 657 | height: u32, 658 | cells: FixedBitSet, 659 | } 660 | ``` 661 | 662 | The Universe constructor can be adjusted the following way: 663 | 664 | ```rust 665 | pub fn new() -> Universe { 666 | let width = 64; 667 | let height = 64; 668 | 669 | let size = (width * height) as usize; 670 | let mut cells = FixedBitSet::with_capacity(size); 671 | 672 | for i in 0..size { 673 | cells.set(i, i % 2 == 0 || i % 7 == 0); 674 | } 675 | 676 | Universe { 677 | width, 678 | height, 679 | cells, 680 | } 681 | } 682 | ``` 683 | 684 | To update a cell in the next tick of the universe, we use the `set` method 685 | of `FixedBitSet`: 686 | 687 | ```rust 688 | next.set(idx, match (cell, live_neighbors) { 689 | (true, x) if x < 2 => false, 690 | (true, 2) | (true, 3) => true, 691 | (true, x) if x > 3 => false, 692 | (false, 3) => true, 693 | (otherwise, _) => otherwise 694 | }); 695 | ``` 696 | 697 | To pass a pointer to the start of the bits to JavaScript, you can convert 698 | the `FixedBitSet` to a slice and then convert the slice to a pointer: 699 | 700 | ```rust 701 | #[wasm_bindgen] 702 | impl Universe { 703 | // ... 704 | 705 | pub fn cells(&self) -> *const u32 { 706 | self.cells.as_slice().as_ptr() 707 | } 708 | } 709 | ``` 710 | 711 | In JavaScript, constructing a `Uint8Array` from Wasm memory is the same as 712 | before, except that the length of the array is not `width * height` anymore, 713 | but `width * height / 8` since we have a cell per bit rather than per byte: 714 | 715 | ```js 716 | const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8); 717 | ``` 718 | 719 | Given an index and `Uint8Array`, you can determine whether the 720 | *nth* bit is set with the following function: 721 | 722 | ```js 723 | const bitIsSet = (n, arr) => { 724 | const byte = Math.floor(n / 8); 725 | const mask = 1 << (n % 8); 726 | return (arr[byte] & mask) === mask; 727 | }; 728 | ``` 729 | 730 | Given all that, the new version of `drawCells` looks like this: 731 | 732 | ```js 733 | const drawCells = () => { 734 | const cellsPtr = universe.cells(); 735 | 736 | // This is updated! 737 | const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8); 738 | 739 | ctx.beginPath(); 740 | 741 | for (let row = 0; row < height; row++) { 742 | for (let col = 0; col < width; col++) { 743 | const idx = getIndex(row, col); 744 | 745 | // This is updated! 746 | ctx.fillStyle = bitIsSet(idx, cells) 747 | ? ALIVE_COLOR 748 | : DEAD_COLOR; 749 | 750 | ctx.fillRect( 751 | col * (CELL_SIZE + 1) + 1, 752 | row * (CELL_SIZE + 1) + 1, 753 | CELL_SIZE, 754 | CELL_SIZE 755 | ); 756 | } 757 | } 758 | 759 | ctx.stroke(); 760 | }; 761 | ``` 762 | 763 |
764 | -------------------------------------------------------------------------------- /src/game-of-life/interactivity.md: -------------------------------------------------------------------------------- 1 | # Adding Interactivity 2 | 3 | We will continue to explore the JavaScript and WebAssembly interface by adding 4 | some interactive features to our Game of Life implementation. We will enable 5 | users to toggle whether a cell is alive or dead by clicking on it, and 6 | allow pausing the game, which makes drawing cell patterns a lot easier. 7 | 8 | ## Pausing and Resuming the Game 9 | 10 | Let's add a button to toggle whether the game is playing or paused. To 11 | `wasm-game-of-life/www/index.html`, add the button right above the ``: 12 | 13 | ```html 14 | 15 | ``` 16 | 17 | In the `wasm-game-of-life/www/index.js` JavaScript, we will make the following 18 | changes: 19 | 20 | * Keep track of the identifier returned by the latest call to 21 | `requestAnimationFrame`, so that we can cancel the animation by calling 22 | `cancelAnimationFrame` with that identifier. 23 | 24 | * When the play/pause button is clicked, check for whether we have the 25 | identifier for a queued animation frame. If we do, then the game is currently 26 | playing, and we want to cancel the animation frame so that `renderLoop` isn't 27 | called again, effectively pausing the game. If we do not have an identifier 28 | for a queued animation frame, then we are currently paused, and we would like 29 | to call `requestAnimationFrame` to resume the game. 30 | 31 | Because the JavaScript is driving the Rust and WebAssembly, this is all we need 32 | to do, and we don't need to change the Rust sources. 33 | 34 | We introduce the `animationId` variable to keep track of the identifier returned 35 | by `requestAnimationFrame`. When there is no queued animation frame, we set this 36 | variable to `null`. 37 | 38 | ```js 39 | let animationId = null; 40 | 41 | // This function is the same as before, except the 42 | // result of `requestAnimationFrame` is assigned to 43 | // `animationId`. 44 | const renderLoop = () => { 45 | drawGrid(); 46 | drawCells(); 47 | 48 | universe.tick(); 49 | 50 | animationId = requestAnimationFrame(renderLoop); 51 | }; 52 | ``` 53 | 54 | At any instant in time, we can tell whether the game is paused or not by 55 | inspecting the value of `animationId`: 56 | 57 | ```js 58 | const isPaused = () => { 59 | return animationId === null; 60 | }; 61 | ``` 62 | 63 | Now, when the play/pause button is clicked, we check whether the game is 64 | currently paused or playing, and resume the `renderLoop` animation or cancel the 65 | next animation frame respectively. Additionally, we update the button's text 66 | icon to reflect the action that the button will take when clicked next. 67 | 68 | ```js 69 | const playPauseButton = document.getElementById("play-pause"); 70 | 71 | const play = () => { 72 | playPauseButton.textContent = "⏸"; 73 | renderLoop(); 74 | }; 75 | 76 | const pause = () => { 77 | playPauseButton.textContent = "▶"; 78 | cancelAnimationFrame(animationId); 79 | animationId = null; 80 | }; 81 | 82 | playPauseButton.addEventListener("click", event => { 83 | if (isPaused()) { 84 | play(); 85 | } else { 86 | pause(); 87 | } 88 | }); 89 | ``` 90 | 91 | Finally, we were previously kick-starting the game and its animation by calling 92 | `requestAnimationFrame(renderLoop)` directly, but we want to replace that with a 93 | call to `play` so that the button gets the correct initial text icon. 94 | 95 | ```diff 96 | // This used to be `requestAnimationFrame(renderLoop)`. 97 | play(); 98 | ``` 99 | 100 | Refresh [http://localhost:8080/](http://localhost:8080/) and we should now be 101 | able to pause and resume the game by clicking on the button! 102 | 103 | ## Toggling a Cell's State on `"click"` Events 104 | 105 | Now that we can pause the game, it's time to add the ability to mutate the cells 106 | by clicking on them. 107 | 108 | To toggle a cell is to flip its state from alive to dead or from dead to 109 | alive. Add a `toggle` method to `Cell` in `wasm-game-of-life/src/lib.rs`: 110 | 111 | ```rust 112 | impl Cell { 113 | fn toggle(&mut self) { 114 | *self = match *self { 115 | Cell::Dead => Cell::Alive, 116 | Cell::Alive => Cell::Dead, 117 | }; 118 | } 119 | } 120 | ``` 121 | 122 | To toggle the state of a cell at given row and column, we translate the row and 123 | column pair into an index into the cells vector and call the toggle method on 124 | the cell at that index: 125 | 126 | ```rust 127 | /// Public methods, exported to JavaScript. 128 | #[wasm_bindgen] 129 | impl Universe { 130 | // ... 131 | 132 | pub fn toggle_cell(&mut self, row: u32, column: u32) { 133 | let idx = self.get_index(row, column); 134 | self.cells[idx].toggle(); 135 | } 136 | } 137 | ``` 138 | 139 | This method is defined within the `impl` block that is annotated with 140 | `#[wasm_bindgen]` so that it can be called by JavaScript. 141 | 142 | In `wasm-game-of-life/www/index.js`, we listen to click events on the `` 143 | element, translate the click event's page-relative coordinates into 144 | canvas-relative coordinates, and then into a row and column, invoke the 145 | `toggle_cell` method, and finally redraw the scene. 146 | 147 | ```js 148 | canvas.addEventListener("click", event => { 149 | const boundingRect = canvas.getBoundingClientRect(); 150 | 151 | const scaleX = canvas.width / boundingRect.width; 152 | const scaleY = canvas.height / boundingRect.height; 153 | 154 | const canvasLeft = (event.clientX - boundingRect.left) * scaleX; 155 | const canvasTop = (event.clientY - boundingRect.top) * scaleY; 156 | 157 | const row = Math.min(Math.floor(canvasTop / (CELL_SIZE + 1)), height - 1); 158 | const col = Math.min(Math.floor(canvasLeft / (CELL_SIZE + 1)), width - 1); 159 | 160 | universe.toggle_cell(row, col); 161 | 162 | drawGrid(); 163 | drawCells(); 164 | }); 165 | ``` 166 | 167 | Rebuild with `wasm-pack build` in `wasm-game-of-life`, then refresh 168 | [http://localhost:8080/](http://localhost:8080/) again and we can now draw our 169 | own patterns by clicking on the cells and toggling their state. 170 | 171 | ## Exercises 172 | 173 | * Introduce an [``][input-range] widget to control how many 174 | ticks occur per animation frame. 175 | 176 | * Add a button that resets the universe to a random initial state when 177 | clicked. Another button that resets the universe to all dead cells. 178 | 179 | * On `Ctrl + Click`, insert a 180 | [glider](https://en.wikipedia.org/wiki/Glider_(Conway%27s_Life)) centered on 181 | the target cell. On `Shift + Click`, insert a pulsar. 182 | 183 | [input-range]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range 184 | -------------------------------------------------------------------------------- /src/game-of-life/introduction.md: -------------------------------------------------------------------------------- 1 | # チュートリアル: ライフゲーム 2 | 3 | 4 | 5 | これは[ライフゲーム][gol]をRust and WebAssemblyで実装するチュートリアルです。 6 | 7 | 9 | 10 | [gol]: https://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B2%E3%83%BC%E3%83%A0 11 | 12 | ## このチュートリアルの対象読者は? 13 | 14 | 15 | 16 | このチュートリアルは既に基本的なRustとJavaScriptの経験があり、RustとWebAssemblyとJavaScriptを組み合わせて使う方法を学びたい人のためのものです。 17 | 18 | 21 | 22 | 読者はRustやJavaScriptやHTMLの基本的な読み書きが気楽にできるべきです。熟練者である必要はまったくありません。 23 | 24 | 26 | 27 | ## 何を学ぶのか? 28 | 29 | 30 | 31 | * WebAssemblyへコンパイルするためにRustのツールチェインをセットアップする方法。 32 | 33 | 34 | 35 | * Rust、WebAssembly、JavaScript、HTML、CSSから成る複数言語プログラムを開発するワークフロー。 36 | 37 | 39 | 40 | * RustとWebAssembly両方の強みを、そしてさらにJavaScriptの強みを最大限に生かすAPIをデザインする方法。 41 | 42 | 44 | 45 | * RustからコンパイルされたWebAssemblyモジュールをデバッグする方法。 46 | 47 | 48 | 49 | * Rust and WebAssemblyのプログラムをより速くするためにプロファイルする方法。 50 | 51 | 52 | 53 | * Rust and WebAssemblyのプログラムをより小さくし、ネットワーク越しのダウンロードをより速くするためのサイズプロファイルの方法。 54 | 55 | 57 | -------------------------------------------------------------------------------- /src/game-of-life/publishing-to-npm.md: -------------------------------------------------------------------------------- 1 | # Publishing to npm 2 | 3 | Now that we have a working, fast, *and* small `wasm-game-of-life` package, we 4 | can publish it to npm so other JavaScript developers can reuse it, if they ever 5 | need an off-the-shelf Game of Life implementation. 6 | 7 | ## Prerequisites 8 | 9 | First, [make sure you have an npm account](https://www.npmjs.com/signup). 10 | 11 | Second, make sure you are logged into your account locally, by running this 12 | command: 13 | 14 | ``` 15 | wasm-pack login 16 | ``` 17 | 18 | ## Publishing 19 | 20 | Make sure that the `wasm-game-of-life/pkg` build is up to date by running 21 | `wasm-pack` inside the `wasm-game-of-life` directory: 22 | 23 | ``` 24 | wasm-pack build 25 | ``` 26 | 27 | Take a moment to check out the contents of `wasm-game-of-life/pkg` now, this is 28 | what we are publishing to npm in the next step! 29 | 30 | When you're ready, run `wasm-pack publish` to upload the package to npm: 31 | 32 | ``` 33 | wasm-pack publish 34 | ``` 35 | 36 | That's all it takes to publish to npm! 37 | 38 | ...except other folks have also done this tutorial, and therefore the 39 | `wasm-game-of-life` name is taken on npm, and that last command probably didn't 40 | work. 41 | 42 | Open up `wasm-game-of-life/Cargo.toml` and add your username to the end of the 43 | `name` to disambiguate the package in a unique way: 44 | 45 | ```toml 46 | [package] 47 | name = "wasm-game-of-life-my-username" 48 | ``` 49 | 50 | Then, rebuild and publish again: 51 | 52 | ``` 53 | wasm-pack build 54 | wasm-pack publish 55 | ``` 56 | 57 | This time it should work! 58 | -------------------------------------------------------------------------------- /src/game-of-life/rules.md: -------------------------------------------------------------------------------- 1 | # Rules of Conway's Game of Life 2 | 3 | *Note: If you are already familiar with Conway's Game of Life and its rules, 4 | feel free to skip to the next section!* 5 | 6 | [Wikipedia gives a great description of the rules of Conway's Game of 7 | Life:][wikipedia] 8 | 9 | > The universe of the Game of Life is an infinite two-dimensional orthogonal 10 | > grid of square cells, each of which is in one of two possible states, alive or 11 | > dead, or "populated" or "unpopulated". Every cell interacts with its eight 12 | > neighbours, which are the cells that are horizontally, vertically, or 13 | > diagonally adjacent. At each step in time, the following transitions occur: 14 | > 15 | > 1. Any live cell with fewer than two live neighbours dies, as if caused by 16 | > underpopulation. 17 | > 18 | > 2. Any live cell with two or three live neighbours lives on to the next 19 | > generation. 20 | > 21 | > 3. Any live cell with more than three live neighbours dies, as if by 22 | > overpopulation. 23 | > 24 | > 4. Any dead cell with exactly three live neighbours becomes a live cell, as if 25 | > by reproduction. 26 | > 27 | > The initial pattern constitutes the seed of the system. The first generation 28 | > is created by applying the above rules simultaneously to every cell in the 29 | > seed—births and deaths occur simultaneously, and the discrete moment at which 30 | > this happens is sometimes called a tick (in other words, each generation is a 31 | > pure function of the preceding one). The rules continue to be applied 32 | > repeatedly to create further generations. 33 | 34 | [wikipedia]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life 35 | 36 | Consider the following initial universe: 37 | 38 | Initial Universe 39 | 40 | We can calculate the next generation by considering each cell. The top left cell 41 | is dead. Rule (4) is the only transition rule that applies to dead 42 | cells. However, because the top left cell does not have exactly three live 43 | neighbors, the transition rule does not apply, and it remains dead in the next 44 | generation. The same goes for every other cell in the first row as well. 45 | 46 | Things get interesting when we consider the top live cell, in the second row, 47 | third column. For live cells, any of the first three rules potentially 48 | applies. In this cell's case, it has only one live neighbor, and therefore rule 49 | (1) applies: this cell will die in the next generation. The same fate awaits the 50 | bottom live cell. 51 | 52 | The middle live cell has two live neighbors: the top and bottom live cells. This 53 | means that rule (2) applies, and it remains live in the next generation. 54 | 55 | The final interesting cases are the dead cells just to the left and right of the 56 | middle live cell. The three live cells are all neighbors both of these cells, 57 | which means that rule (4) applies, and these cells will become alive in the next 58 | generation. 59 | 60 | Put it all together, and we get this universe after the next tick: 61 | 62 | Next Universe 63 | 64 | From these simple, deterministic rules, strange and exciting behavior emerges: 65 | 66 | | Gosper's glider gun | Pulsar | Space ship | 67 | |---|---|---| 68 | | ![Gosper's glider gun](https://upload.wikimedia.org/wikipedia/commons/e/e5/Gospers_glider_gun.gif) | ![Pulsar](https://upload.wikimedia.org/wikipedia/commons/0/07/Game_of_life_pulsar.gif) | ![Lighweight space ship](https://upload.wikimedia.org/wikipedia/commons/3/37/Game_of_life_animated_LWSS.gif) | 69 | 70 |
71 | 72 |
73 | 74 | ## Exercises 75 | 76 | * Compute by hand the next tick of our example universe. Notice anything 77 | familiar? 78 | 79 |
80 | Answer 81 | 82 | It should be the initial state of the example universe: 83 | 84 | Initial Universe 85 | 86 | This pattern is *periodic*: it returns to the initial state after every two 87 | ticks. 88 | 89 |
90 | 91 | * Can you find an initial universe that is stable? That is, a universe in which 92 | every generation is always the same. 93 | 94 |
95 | Answer 96 | 97 | There are an infinite number of stable universes! The trivially stable 98 | universe is the empty universe. A two-by-two square of live cells is also a 99 | stable universe. 100 | 101 |
102 | -------------------------------------------------------------------------------- /src/game-of-life/setup.md: -------------------------------------------------------------------------------- 1 | # セットアップ 2 | 3 | 4 | 5 | このセクションはRustプログラムをWebAssemblyにコンパイルし、JavaScriptと組み合わせる方法を記述します。 6 | 7 | 9 | 10 | ## Rustツールチェイン 11 | 12 | 13 | 14 | `rustup`、`rustc`、`cargo`を含むツールチェインが必要になります。 15 | 16 | 18 | 19 | [Rustツールチェインをインストールするためにこれらの手順に従ってください。][rust-install] 20 | 21 | 22 | 23 | Rust and WebAssemblyの体験はstableのリリース列車に乗っています!それは実験的機能は要求しないということです。しかし、Rust 1.30またはそれ以上の新しいバージョンが要求されます。 24 | 25 | 28 | 29 | ## `wasm-pack` 30 | 31 | 32 | 33 | `wasm-pack`はRustにより生成されたWebAssemblyのビルド、テスト、パブリッシュのためのワンストップショップ (訳注: そこだけで全ての必要な買い物ができるような場所のこと) です。 34 | 35 | 37 | 38 | [ここで`wasm-pack`をゲットしましょう!][wasm-pack-install] 39 | 40 | 41 | 42 | ## `cargo-generate` 43 | 44 | 45 | 46 | [`cargo-generate`は既に存在しているGitリポジトリをテンプレートとして利用して新しいRustプロジェクトを迅速に立ち上げ、実行に移すことを助けます。][cargo-generate] 47 | 48 | 50 | 51 | `cargo-generate`をこのコマンドでインストールしてください: 52 | 53 | 54 | 55 | ``` 56 | cargo install cargo-generate 57 | ``` 58 | 59 | ## `npm` 60 | 61 | `npm`はJavaScriptのパッケージマネージャです。JavaScriptバンドラと開発サーバをインストールし、実行するために使用します。チュートリアルの最後に、コンパイルした`.wasm`を`npm`レジストリにパブリッシュします。 62 | 63 | 66 | 67 | [これらの手順に従って`npm`をインストールしてください。][npm-install] 68 | 69 | 70 | 71 | 既に`npm`をインストールしている場合、このコマンドで最新版であるか確認してください: 72 | 73 | 75 | 76 | ``` 77 | npm install npm@latest -g 78 | ``` 79 | 80 | [rust-install]: https://www.rust-lang.org/tools/install 81 | [npm-install]: https://www.npmjs.com/get-npm 82 | [wasm-pack]: https://github.com/rustwasm/wasm-pack 83 | [cargo-generate]: https://github.com/ashleygwilliams/cargo-generate 84 | [wasm-pack-install]: https://rustwasm.github.io/wasm-pack/installer/ 85 | -------------------------------------------------------------------------------- /src/game-of-life/testing.md: -------------------------------------------------------------------------------- 1 | # Testing Conway's Game of Life 2 | 3 | Now that we have our Rust implementation of the Game of Life rendering in the 4 | browser with JavaScript, let's talk about testing our Rust-generated 5 | WebAssembly functions. 6 | 7 | We are going to test our `tick` function to make sure that it gives us the 8 | output that we expect. 9 | 10 | Next, we'll want to create some setter and getter 11 | functions inside our existing `impl Universe` block in the 12 | `wasm_game_of_life/src/lib.rs` file. We are going to create a `set_width` 13 | and a `set_height` function so we can create `Universe`s of different sizes. 14 | 15 | ```rust 16 | #[wasm_bindgen] 17 | impl Universe { 18 | // ... 19 | 20 | /// Set the width of the universe. 21 | /// 22 | /// Resets all cells to the dead state. 23 | pub fn set_width(&mut self, width: u32) { 24 | self.width = width; 25 | self.cells = (0..width * self.height).map(|_i| Cell::Dead).collect(); 26 | } 27 | 28 | /// Set the height of the universe. 29 | /// 30 | /// Resets all cells to the dead state. 31 | pub fn set_height(&mut self, height: u32) { 32 | self.height = height; 33 | self.cells = (0..self.width * height).map(|_i| Cell::Dead).collect(); 34 | } 35 | 36 | } 37 | ``` 38 | 39 | We are going to create another `impl Universe` block inside our 40 | `wasm_game_of_life/src/lib.rs` file without the `#[wasm_bindgen]` attribute. 41 | There are a few functions we need for testing that we don't want to expose to 42 | our JavaScript. Rust-generated WebAssembly functions cannot return 43 | borrowed references. Try compiling the Rust-generated WebAssembly with the 44 | attribute and take a look at the errors you get. 45 | 46 | We are going to write the implementation of `get_cells` to get the contents of 47 | the `cells` of a `Universe`. We'll also write a `set_cells` function so we can 48 | set `cells` in a specific row and column of a `Universe` to be `Alive.` 49 | 50 | ```rust 51 | impl Universe { 52 | /// Get the dead and alive values of the entire universe. 53 | pub fn get_cells(&self) -> &[Cell] { 54 | &self.cells 55 | } 56 | 57 | /// Set cells to be alive in a universe by passing the row and column 58 | /// of each cell as an array. 59 | pub fn set_cells(&mut self, cells: &[(u32, u32)]) { 60 | for (row, col) in cells.iter().cloned() { 61 | let idx = self.get_index(row, col); 62 | self.cells[idx] = Cell::Alive; 63 | } 64 | } 65 | 66 | } 67 | ``` 68 | 69 | Now we're going to create our test in the `wasm_game_of_life/tests/web.rs` file. 70 | 71 | Before we do that, there is already one working test in the file. You can 72 | confirm that the Rust-generated WebAssembly test is working by running 73 | `wasm-pack test --chrome --headless` in the `wasm-game-of-life` directory. 74 | You can also use the `--firefox`, `--safari`, and `--node` options to 75 | test your code in those browsers. 76 | 77 | In the `wasm_game_of_life/tests/web.rs` file, we need to export our 78 | `wasm_game_of_life` crate and the `Universe` type. 79 | 80 | ```rust 81 | extern crate wasm_game_of_life; 82 | use wasm_game_of_life::Universe; 83 | ``` 84 | 85 | In the `wasm_game_of_life/tests/web.rs` file we'll want to create some 86 | spaceship builder functions. 87 | 88 | We'll want one for our input spaceship that we'll call the `tick` function on 89 | and we'll want the expected spaceship we will get after one tick. We picked the 90 | cells that we want to initialize as `Alive` to create our spaceship in the 91 | `input_spaceship` function. The position of the spaceship in the 92 | `expected_spaceship` function after the tick of the `input_spaceship` was 93 | calculated manually. You can confirm for yourself that the cells of the input 94 | spaceship after one tick is the same as the expected spaceship. 95 | 96 | ```rust 97 | #[cfg(test)] 98 | pub fn input_spaceship() -> Universe { 99 | let mut universe = Universe::new(); 100 | universe.set_width(6); 101 | universe.set_height(6); 102 | universe.set_cells(&[(1,2), (2,3), (3,1), (3,2), (3,3)]); 103 | universe 104 | } 105 | 106 | #[cfg(test)] 107 | pub fn expected_spaceship() -> Universe { 108 | let mut universe = Universe::new(); 109 | universe.set_width(6); 110 | universe.set_height(6); 111 | universe.set_cells(&[(2,1), (2,3), (3,2), (3,3), (4,2)]); 112 | universe 113 | } 114 | ``` 115 | 116 | Now we will write the implementation for our `test_tick` function. First, we 117 | create an instance of our `input_spaceship()` and our `expected_spaceship()`. 118 | Then, we call `tick` on the `input_universe`. Finally, we use the `assert_eq!` 119 | macro to call `get_cells()` to ensure that `input_universe` and 120 | `expected_universe` have the same `Cell` array values. We add the 121 | `#[wasm_bindgen_test]` attribute to our code block so we can test our 122 | Rust-generated WebAssembly code and use `wasm-build test` to test the 123 | WebAssembly code. 124 | 125 | ```rust 126 | #[wasm_bindgen_test] 127 | pub fn test_tick() { 128 | // Let's create a smaller Universe with a small spaceship to test! 129 | let mut input_universe = input_spaceship(); 130 | 131 | // This is what our spaceship should look like 132 | // after one tick in our universe. 133 | let expected_universe = expected_spaceship(); 134 | 135 | // Call `tick` and then see if the cells in the `Universe`s are the same. 136 | input_universe.tick(); 137 | assert_eq!(&input_universe.get_cells(), &expected_universe.get_cells()); 138 | } 139 | ``` 140 | 141 | Run the tests within the `wasm-game-of-life` directory by running 142 | `wasm-pack test --firefox --headless`. 143 | -------------------------------------------------------------------------------- /src/game-of-life/time-profiling.md: -------------------------------------------------------------------------------- 1 | # Time Profiling 2 | 3 | In this chapter, we will improve the performance of our Game of Life 4 | implementation. We will use time profiling to guide our efforts. 5 | 6 | Familiarize yourself with [the available tools for time profiling Rust and 7 | WebAssembly code](../reference/time-profiling.md) before continuing. 8 | 9 | ## Creating a Frames Per Second Timer with the `window.performance.now` Function 10 | 11 | This FPS timer will be useful as we investigate speeding up our Game of Life's 12 | rendering. 13 | 14 | We start by adding an `fps` object to `wasm-game-of-life/www/index.js`: 15 | 16 | ```js 17 | const fps = new class { 18 | constructor() { 19 | this.fps = document.getElementById("fps"); 20 | this.frames = []; 21 | this.lastFrameTimeStamp = performance.now(); 22 | } 23 | 24 | render() { 25 | // Convert the delta time since the last frame render into a measure 26 | // of frames per second. 27 | const now = performance.now(); 28 | const delta = now - this.lastFrameTimeStamp; 29 | this.lastFrameTimeStamp = now; 30 | const fps = 1 / delta * 1000; 31 | 32 | // Save only the latest 100 timings. 33 | this.frames.push(fps); 34 | if (this.frames.length > 100) { 35 | this.frames.shift(); 36 | } 37 | 38 | // Find the max, min, and mean of our 100 latest timings. 39 | let min = Infinity; 40 | let max = -Infinity; 41 | let sum = 0; 42 | for (let i = 0; i < this.frames.length; i++) { 43 | sum += this.frames[i]; 44 | min = Math.min(this.frames[i], min); 45 | max = Math.max(this.frames[i], max); 46 | } 47 | let mean = sum / this.frames.length; 48 | 49 | // Render the statistics. 50 | this.fps.textContent = ` 51 | Frames per Second: 52 | latest = ${Math.round(fps)} 53 | avg of last 100 = ${Math.round(mean)} 54 | min of last 100 = ${Math.round(min)} 55 | max of last 100 = ${Math.round(max)} 56 | `.trim(); 57 | } 58 | }; 59 | ``` 60 | 61 | Next we call the `fps` `render` function on each iteration of `renderLoop`: 62 | 63 | ```js 64 | const renderLoop = () => { 65 | fps.render(); //new 66 | 67 | universe.tick(); 68 | drawGrid(); 69 | drawCells(); 70 | 71 | animationId = requestAnimationFrame(renderLoop); 72 | }; 73 | ``` 74 | 75 | Finally, don't forget to add the `fps` element to 76 | `wasm-game-of-life/www/index.html`, just above the ``: 77 | 78 | ```html 79 |
80 | ``` 81 | 82 | And add CSS to make its formatting nice: 83 | 84 | ```css 85 | #fps { 86 | white-space: pre; 87 | font-family: monospace; 88 | } 89 | ``` 90 | 91 | And voila! Refresh [http://localhost:8080](http://localhost:8080) and now we 92 | have an FPS counter! 93 | 94 | [perf-now]: https://developer.mozilla.org/en-US/docs/Web/API/Performance/now 95 | 96 | ### Time Each `Universe::tick` with `console.time` and `console.timeEnd` 97 | 98 | To measure how long each invocation of `Universe::tick` takes, we can use 99 | `console.time` and `console.timeEnd` via the `web-sys` crate. 100 | 101 | First, add `web-sys` as a dependency to `wasm-game-of-life/Cargo.toml`: 102 | 103 | ```toml 104 | [dependencies.web-sys] 105 | version = "0.3" 106 | features = [ 107 | "console", 108 | ] 109 | ``` 110 | 111 | Because there should be a corresponding `console.timeEnd` invocation for every 112 | `console.time` call, it is convenient to wrap them both up in an [RAII][] type: 113 | 114 | ```rust 115 | extern crate web_sys; 116 | use web_sys::console; 117 | 118 | pub struct Timer<'a> { 119 | name: &'a str, 120 | } 121 | 122 | impl<'a> Timer<'a> { 123 | pub fn new(name: &'a str) -> Timer<'a> { 124 | console::time_with_label(name); 125 | Timer { name } 126 | } 127 | } 128 | 129 | impl<'a> Drop for Timer<'a> { 130 | fn drop(&mut self) { 131 | console::time_end_with_label(self.name); 132 | } 133 | } 134 | ``` 135 | 136 | Then, we can time how long each `Universe::tick` takes by adding this snippet to 137 | the top of the method: 138 | 139 | ```rust 140 | let _timer = Timer::new("Universe::tick"); 141 | ``` 142 | 143 | The time of how long each call to `Universe::tick` took are now logged in the 144 | console: 145 | 146 | [![Screenshot of console.time logs](../images/game-of-life/console-time.png)](../images/game-of-life/console-time.png) 147 | 148 | Additionally, `console.time` and `console.timeEnd` pairs will show up in your 149 | browser's profiler's "timeline" or "waterfall" view: 150 | pp 151 | [![Screenshot of console.time logs](../images/game-of-life/console-time-in-profiler.png)](../images/game-of-life/console-time-in-profiler.png) 152 | 153 | [RAII]: https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization 154 | 155 | ## Growing our Game of Life Universe 156 | 157 | > ⚠️ This section utilizes example screenshots from Firefox. While all modern 158 | > browsers have similar tools, there might be slight nuances to working with 159 | > different developer tools. The profile information you extract will be 160 | > essentially the same, but your mileage might vary in terms of the views you 161 | > see and the naming of different tools. 162 | 163 | What happens if we make our Game of Life universe larger? Replacing the 64 by 64 164 | universe with a 128 by 128 universe (by modifying `Universe::new` in 165 | `wasm-game-of-life/src/lib.rs`) results in FPS dropping from a smooth 60 to a 166 | choppy 40-ish on my machine. 167 | 168 | If we record a profile and look at the waterfall view, we see that each 169 | animation frame is taking over 20 milliseconds. Recall that 60 frames per second 170 | leaves sixteen milliseconds for the whole process of rendering a frame. That's 171 | not just our JavaScript and WebAssembly, but also everything else the browser is 172 | doing, such as painting. 173 | 174 | [![Screenshot of a waterfall view of rendering a frame](../images/game-of-life/drawCells-before-waterfall.png)](../images/game-of-life/drawCells-before-waterfall.png) 175 | 176 | If we look at what happens within a single animation frame, we see that the 177 | `CanvasRenderingContext2D.fillStyle` setter is very expensive! 178 | 179 | > ⚠️ In Firefox, if you see a line that simply says "DOM" instead of the 180 | > `CanvasRenderingContext2D.fillStyle` mentioned above, you may need to turn on 181 | > the option for "Show Gecko Platform Data" in your performance developer tools 182 | > options: 183 | > 184 | > [![Turning on Show Gecko Platform Data](../images/game-of-life/profiler-firefox-show-gecko-platform.png)](../images/game-of-life/profiler-firefox-show-gecko-platform.png) 185 | 186 | [![Screenshot of a flamegraph view of rendering a frame](../images/game-of-life/drawCells-before-flamegraph.png)](../images/game-of-life/drawCells-before-flamegraph.png) 187 | 188 | And we can confirm that this isn't an abnormality by looking at the call tree's 189 | aggregation of many frames: 190 | 191 | [![Screenshot of a flamegraph view of rendering a frame](../images/game-of-life/drawCells-before-calltree.png)](../images/game-of-life/drawCells-before-calltree.png) 192 | 193 | Nearly 40% of our time is spent in this setter! 194 | 195 | > ⚡ We might have expected something in the `tick` method to be the performance 196 | > bottleneck, but it wasn't. Always let profiling guide your focus, since time 197 | > may be spent in places you don't expect it to be. 198 | 199 | In the `drawCells` function in `wasm-game-of-life/www/index.js`, the `fillStyle` 200 | property is set once for every cell in the universe, on every animation frame: 201 | 202 | ```js 203 | for (let row = 0; row < height; row++) { 204 | for (let col = 0; col < width; col++) { 205 | const idx = getIndex(row, col); 206 | 207 | ctx.fillStyle = cells[idx] === DEAD 208 | ? DEAD_COLOR 209 | : ALIVE_COLOR; 210 | 211 | ctx.fillRect( 212 | col * (CELL_SIZE + 1) + 1, 213 | row * (CELL_SIZE + 1) + 1, 214 | CELL_SIZE, 215 | CELL_SIZE 216 | ); 217 | } 218 | } 219 | ``` 220 | 221 | Now that we have discovered that setting `fillStyle` is so expensive, what can 222 | we do to avoid setting it so often? We need to change `fillStyle` depending on 223 | whether a cell is alive or dead. If we set `fillStyle = ALIVE_COLOR` and then 224 | draw every alive cell in one pass, and then set `fillStyle = DEAD_COLOR` and 225 | draw every dead cell in another pass, then we only end setting `fillStyle` 226 | twice, rather than once for every cell. 227 | 228 | ```js 229 | // Alive cells. 230 | ctx.fillStyle = ALIVE_COLOR; 231 | for (let row = 0; row < height; row++) { 232 | for (let col = 0; col < width; col++) { 233 | const idx = getIndex(row, col); 234 | if (cells[idx] !== Cell.Alive) { 235 | continue; 236 | } 237 | 238 | ctx.fillRect( 239 | col * (CELL_SIZE + 1) + 1, 240 | row * (CELL_SIZE + 1) + 1, 241 | CELL_SIZE, 242 | CELL_SIZE 243 | ); 244 | } 245 | } 246 | 247 | // Dead cells. 248 | ctx.fillStyle = DEAD_COLOR; 249 | for (let row = 0; row < height; row++) { 250 | for (let col = 0; col < width; col++) { 251 | const idx = getIndex(row, col); 252 | if (cells[idx] !== Cell.Dead) { 253 | continue; 254 | } 255 | 256 | ctx.fillRect( 257 | col * (CELL_SIZE + 1) + 1, 258 | row * (CELL_SIZE + 1) + 1, 259 | CELL_SIZE, 260 | CELL_SIZE 261 | ); 262 | } 263 | } 264 | ``` 265 | 266 | After saving these changes and refreshing 267 | [http://localhost:8080/](http://localhost:8080/), rendering is back to a smooth 268 | 60 frames per second. 269 | 270 | If we take another profile, we can see that only about ten milliseconds are 271 | spent in each animation frame now. 272 | 273 | [![Screenshot of a waterfall view of rendering a frame after the drawCells changes](../images/game-of-life/drawCells-after-waterfall.png)](../images/game-of-life/drawCells-after-waterfall.png) 274 | 275 | Breaking down a single frame, we see that the `fillStyle` cost is gone, and most 276 | of our frame's time is spent within `fillRect`, drawing each cell's rectangle. 277 | 278 | [![Screenshot of a flamegraph view of rendering a frame after the drawCells changes](../images/game-of-life/drawCells-after-flamegraph.png)](../images/game-of-life/drawCells-after-flamegraph.png) 279 | 280 | ## Making Time Run Faster 281 | 282 | Some folks don't like waiting around, and would prefer if instead of one tick of 283 | the universe occurred per animation frame, nine ticks did. We can modify the 284 | `renderLoop` function in `wasm-game-of-life/www/index.js` to do this quite 285 | easily: 286 | 287 | ```js 288 | for (let i = 0; i < 9; i++) { 289 | universe.tick(); 290 | } 291 | ``` 292 | 293 | On my machine, this brings us back down to only 35 frames per second. No 294 | good. We want that buttery 60! 295 | 296 | Now we know that time is being spent in `Universe::tick`, so let's add some 297 | `Timer`s to wrap various bits of it in `console.time` and `console.timeEnd` 298 | calls, and see where that leads us. My hypothesis is that allocating a new 299 | vector of cells and freeing the old vector on every tick is costly, and taking 300 | up a significant portion of our time budget. 301 | 302 | ```rust 303 | pub fn tick(&mut self) { 304 | let _timer = Timer::new("Universe::tick"); 305 | 306 | let mut next = { 307 | let _timer = Timer::new("allocate next cells"); 308 | self.cells.clone() 309 | }; 310 | 311 | { 312 | let _timer = Timer::new("new generation"); 313 | for row in 0..self.height { 314 | for col in 0..self.width { 315 | let idx = self.get_index(row, col); 316 | let cell = self.cells[idx]; 317 | let live_neighbors = self.live_neighbor_count(row, col); 318 | 319 | let next_cell = match (cell, live_neighbors) { 320 | // Rule 1: Any live cell with fewer than two live neighbours 321 | // dies, as if caused by underpopulation. 322 | (Cell::Alive, x) if x < 2 => Cell::Dead, 323 | // Rule 2: Any live cell with two or three live neighbours 324 | // lives on to the next generation. 325 | (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, 326 | // Rule 3: Any live cell with more than three live 327 | // neighbours dies, as if by overpopulation. 328 | (Cell::Alive, x) if x > 3 => Cell::Dead, 329 | // Rule 4: Any dead cell with exactly three live neighbours 330 | // becomes a live cell, as if by reproduction. 331 | (Cell::Dead, 3) => Cell::Alive, 332 | // All other cells remain in the same state. 333 | (otherwise, _) => otherwise, 334 | }; 335 | 336 | next[idx] = next_cell; 337 | } 338 | } 339 | } 340 | 341 | let _timer = Timer::new("free old cells"); 342 | self.cells = next; 343 | } 344 | ``` 345 | 346 | Looking at the timings, it is clear that my hypothesis is incorrect: the vast 347 | majority of time is spent actually calculating the next generation of 348 | cells. Allocating and freeing a vector on every tick appears to have negligible 349 | cost, surprisingly. Another reminder to always guide our efforts with profiling! 350 | 351 | [![Screenshot of a Universe::tick timer results](../images/game-of-life/console-time-in-universe-tick.png)](../images/game-of-life/console-time-in-universe-tick.png) 352 | 353 | The next section requires the `nightly` compiler. It's required because of 354 | the [test feature gate](https://doc.rust-lang.org/unstable-book/library-features/test.html) 355 | we're going to use for the benchmarks. Another tool we will install is [cargo benchcmp][benchcmp]. 356 | It's a small utility for comparing micro-benchmarks produced by `cargo bench`. 357 | 358 | [benchcmp]: https://github.com/BurntSushi/cargo-benchcmp 359 | 360 | Let's write a native code `#[bench]` doing the same thing that our WebAssembly 361 | is doing, but where we can use more mature profiling tools. Here is the new 362 | `wasm-game-of-life/benches/bench.rs`: 363 | 364 | ```rust 365 | #![feature(test)] 366 | 367 | extern crate test; 368 | extern crate wasm_game_of_life; 369 | 370 | #[bench] 371 | fn universe_ticks(b: &mut test::Bencher) { 372 | let mut universe = wasm_game_of_life::Universe::new(); 373 | 374 | b.iter(|| { 375 | universe.tick(); 376 | }); 377 | } 378 | ``` 379 | 380 | We also have to comment out all the `#[wasm_bindgen]` annotations, and the 381 | `"cdylib"` bits from `Cargo.toml` or else building native code will fail and 382 | have link errors. 383 | 384 | With all that in place, we can run `cargo bench | tee before.txt` to compile and run our 385 | benchmark! The `| tee before.txt` part will take the output from `cargo bench` and put in a file 386 | called `before.txt`. 387 | 388 | ``` 389 | $ cargo bench | tee before.txt 390 | Finished release [optimized + debuginfo] target(s) in 0.0 secs 391 | Running target/release/deps/wasm_game_of_life-91574dfbe2b5a124 392 | 393 | running 0 tests 394 | 395 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 396 | 397 | Running target/release/deps/bench-8474091a05cfa2d9 398 | 399 | running 1 test 400 | test universe_ticks ... bench: 664,421 ns/iter (+/- 51,926) 401 | 402 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out 403 | ``` 404 | 405 | This also tells us where the binary lives, and we can run the benchmarks again, 406 | but this time under our operating system's profiler. In my case, I'm running 407 | Linux, so [`perf`][perf] is the profiler I'll use: 408 | 409 | [perf]: https://perf.wiki.kernel.org/index.php/Main_Page 410 | 411 | ``` 412 | $ perf record -g target/release/deps/bench-8474091a05cfa2d9 --bench 413 | running 1 test 414 | test universe_ticks ... bench: 635,061 ns/iter (+/- 38,764) 415 | 416 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out 417 | 418 | [ perf record: Woken up 1 times to write data ] 419 | [ perf record: Captured and wrote 0.178 MB perf.data (2349 samples) ] 420 | ``` 421 | 422 | Loading up the profile with `perf report` shows that all of our time is spent in 423 | `Universe::tick`, as expected: 424 | 425 | [![Screenshot of perf report](../images/game-of-life/bench-perf-report.png)](../images/game-of-life/bench-perf-report.png) 426 | 427 | `perf` will annotate which instructions in a function time is being spent at if 428 | you press `a`: 429 | 430 | [![Screenshot of perf's instruction annotation](../images/game-of-life/bench-perf-annotate.png)](../images/game-of-life/bench-perf-annotate.png) 431 | 432 | This tells us that 26.67% of time is being spent summing neighboring cells' 433 | values, 23.41% of time is spent getting the neighbor's column index, and another 434 | 15.42% of time is spent getting the neighbor's row index. Of these top three 435 | most expensive instructions, the second and third are both costly `div` 436 | instructions. These `div`s implement the modulo indexing logic in 437 | `Universe::live_neighbor_count`. 438 | 439 | Recall the `live_neighbor_count` definition inside 440 | `wasm-game-of-life/src/lib.rs`: 441 | 442 | ```rust 443 | fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { 444 | let mut count = 0; 445 | for delta_row in [self.height - 1, 0, 1].iter().cloned() { 446 | for delta_col in [self.width - 1, 0, 1].iter().cloned() { 447 | if delta_row == 0 && delta_col == 0 { 448 | continue; 449 | } 450 | 451 | let neighbor_row = (row + delta_row) % self.height; 452 | let neighbor_col = (column + delta_col) % self.width; 453 | let idx = self.get_index(neighbor_row, neighbor_col); 454 | count += self.cells[idx] as u8; 455 | } 456 | } 457 | count 458 | } 459 | ``` 460 | 461 | The reason we used modulo was to avoid cluttering up the code with `if` branches 462 | for the first or last row or column edge cases. But we are paying the cost of a 463 | `div` instruction even for the most common case, when neither `row` nor `column` 464 | is on the edge of the universe and they don't need the modulo wrapping 465 | treatment. Instead, if we use `if`s for the edge cases and unroll this loop, the 466 | branches *should* be very well-predicted by the CPU's branch predictor. 467 | 468 | Let's rewrite `live_neighbor_count` like this: 469 | 470 | ```rust 471 | fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { 472 | let mut count = 0; 473 | 474 | let north = if row == 0 { 475 | self.height - 1 476 | } else { 477 | row - 1 478 | }; 479 | 480 | let south = if row == self.height - 1 { 481 | 0 482 | } else { 483 | row + 1 484 | }; 485 | 486 | let west = if column == 0 { 487 | self.width - 1 488 | } else { 489 | column - 1 490 | }; 491 | 492 | let east = if column == self.width - 1 { 493 | 0 494 | } else { 495 | column + 1 496 | }; 497 | 498 | let nw = self.get_index(north, west); 499 | count += self.cells[nw] as u8; 500 | 501 | let n = self.get_index(north, column); 502 | count += self.cells[n] as u8; 503 | 504 | let ne = self.get_index(north, east); 505 | count += self.cells[ne] as u8; 506 | 507 | let w = self.get_index(row, west); 508 | count += self.cells[w] as u8; 509 | 510 | let e = self.get_index(row, east); 511 | count += self.cells[e] as u8; 512 | 513 | let sw = self.get_index(south, west); 514 | count += self.cells[sw] as u8; 515 | 516 | let s = self.get_index(south, column); 517 | count += self.cells[s] as u8; 518 | 519 | let se = self.get_index(south, east); 520 | count += self.cells[se] as u8; 521 | 522 | count 523 | } 524 | ``` 525 | 526 | Now let's run the benchmarks again! This time output it to `after.txt`. 527 | 528 | ``` 529 | $ cargo bench | tee after.txt 530 | Compiling wasm_game_of_life v0.1.0 (file:///home/fitzgen/wasm_game_of_life) 531 | Finished release [optimized + debuginfo] target(s) in 0.82 secs 532 | Running target/release/deps/wasm_game_of_life-91574dfbe2b5a124 533 | 534 | running 0 tests 535 | 536 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 537 | 538 | Running target/release/deps/bench-8474091a05cfa2d9 539 | 540 | running 1 test 541 | test universe_ticks ... bench: 87,258 ns/iter (+/- 14,632) 542 | 543 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out 544 | ``` 545 | 546 | That looks a whole lot better! We can see just how much better it is with the `benchcmp` tool and the two text files we created before: 547 | 548 | ``` 549 | $ cargo benchcmp before.txt after.txt 550 | name before.txt ns/iter after.txt ns/iter diff ns/iter diff % speedup 551 | universe_ticks 664,421 87,258 -577,163 -86.87% x 7.61 552 | ``` 553 | 554 | Wow! 7.61x speed up! 555 | 556 | WebAssembly intentionally maps closely to common hardware architectures, but we 557 | do need to make sure that this native code speed up translates into a 558 | WebAssembly speed up as well. 559 | 560 | Let's rebuild the `.wasm` with `wasm-pack build` and refresh 561 | [http://localhost:8080/](http://localhost:8080/). On my machine, the page is 562 | running at 60 frames per second again, and recording another profile with the 563 | browser's profiler reveals that each animation frame is taking about ten 564 | milliseconds. 565 | 566 | Success! 567 | 568 | [![Screenshot of a waterfall view of rendering a frame after replacing modulos with branches](../images/game-of-life/waterfall-after-branches-and-unrolling.png)](../images/game-of-life/waterfall-after-branches-and-unrolling.png) 569 | 570 | ## Exercises 571 | 572 | * At this point, the next lowest hanging fruit for speeding up `Universe::tick` 573 | is removing the allocation and free. Implement double buffering of cells, 574 | where the `Universe` maintains two vectors, never frees either of them, and 575 | never allocates new buffers in `tick`. 576 | 577 | * Implement the alternative, delta-based design from the "Implementing Life" 578 | chapter, where the Rust code returns a list of cells that changed states to 579 | JavaScript. Does this make rendering to `` faster? Can you implement 580 | this design without allocating a new list of deltas on every tick? 581 | 582 | * As our profiling has shown us, 2D `` rendering is not particularly 583 | fast. Replace the 2D canvas renderer with a [WebGL][webgl] renderer. How much faster is 584 | the WebGL version? How large can you make the universe before WebGL rendering 585 | is a bottleneck? 586 | 587 | [webgl]: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API 588 | -------------------------------------------------------------------------------- /src/images/game-of-life/bench-perf-annotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/bench-perf-annotate.png -------------------------------------------------------------------------------- /src/images/game-of-life/bench-perf-report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/bench-perf-report.png -------------------------------------------------------------------------------- /src/images/game-of-life/console-time-in-profiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/console-time-in-profiler.png -------------------------------------------------------------------------------- /src/images/game-of-life/console-time-in-universe-tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/console-time-in-universe-tick.png -------------------------------------------------------------------------------- /src/images/game-of-life/console-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/console-time.png -------------------------------------------------------------------------------- /src/images/game-of-life/debugging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/debugging.png -------------------------------------------------------------------------------- /src/images/game-of-life/drawCells-after-flamegraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/drawCells-after-flamegraph.png -------------------------------------------------------------------------------- /src/images/game-of-life/drawCells-after-waterfall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/drawCells-after-waterfall.png -------------------------------------------------------------------------------- /src/images/game-of-life/drawCells-before-calltree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/drawCells-before-calltree.png -------------------------------------------------------------------------------- /src/images/game-of-life/drawCells-before-flamegraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/drawCells-before-flamegraph.png -------------------------------------------------------------------------------- /src/images/game-of-life/drawCells-before-waterfall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/drawCells-before-waterfall.png -------------------------------------------------------------------------------- /src/images/game-of-life/hello-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/hello-world.png -------------------------------------------------------------------------------- /src/images/game-of-life/initial-game-of-life-pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/initial-game-of-life-pre.png -------------------------------------------------------------------------------- /src/images/game-of-life/initial-game-of-life.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/initial-game-of-life.png -------------------------------------------------------------------------------- /src/images/game-of-life/initial-universe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/initial-universe.png -------------------------------------------------------------------------------- /src/images/game-of-life/next-universe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/next-universe.png -------------------------------------------------------------------------------- /src/images/game-of-life/perf-report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/perf-report.png -------------------------------------------------------------------------------- /src/images/game-of-life/profiler-firefox-show-gecko-platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/profiler-firefox-show-gecko-platform.png -------------------------------------------------------------------------------- /src/images/game-of-life/profiler-with-rust-names.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/profiler-with-rust-names.png -------------------------------------------------------------------------------- /src/images/game-of-life/universe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/universe.png -------------------------------------------------------------------------------- /src/images/game-of-life/waterfall-after-branches-and-unrolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/game-of-life/waterfall-after-branches-and-unrolling.png -------------------------------------------------------------------------------- /src/images/wasm_hello_world_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshg/rustwasm-book-ja/22124165ffc5e48af11a4a3a11fb0907f44c70e1/src/images/wasm_hello_world_screenshot.png -------------------------------------------------------------------------------- /src/introduction.md: -------------------------------------------------------------------------------- 1 | # Rust🦀 and WebAssembly🕸 2 | 3 | 4 | 5 | この小さな本は[Rust][]と[WebAssembly][]を組み合わせて使う方法を書いています。 6 | 7 | 8 | 9 | ## この本の対象読者は? 10 | 11 | 12 | 13 | この本は速く信頼性のあるウェブのコードのために、RustをWebAssemblyにコンパイルすることに興味があるすべての人に向けた本です。読者はある程度Rustを知っていて、JavaScriptやHTMLやCSSに親しみを持っていることが推奨されます。しかし、それらのエキスパートである必要はありません。 14 | 15 | 18 | 19 | まだRustを知りませんか?[プログラミング言語Rustから始めましょう。][trpl] 20 | 21 | 22 | 23 | JavaScriptやHTMLやCSSを知りませんか?[MDNでそれらを学びましょう。][mdn] 24 | 25 | 26 | 27 | ## この本の読み方 28 | 29 | 30 | 31 | 初めに[背景とコンセプト][background]に親しみを持ち、そして[RustとWebAssemblyを組み合わせて使う動機][why-rust-wasm]も同様に読むのがよいでしょう。 32 | 33 | 36 | 37 | [チュートリアル][tutorial]は最初から最後まで読むように書かれています。読者はチュートリアルのコードを自分で書き、コンパイルし、実行しながら追いかけることが望ましいです。 38 | もしこれまでにRustとWebAssemblyを組み合わせて使ったことがないのならチュートリアルをやりましょう! 39 | 40 | 43 | 44 | [リファレンスの節][reference]は任意の順序で読むことができます。 45 | 46 | 47 | 48 | > **💡 Tip:** ページ上部の🔍アイコンをクリックまたはキーボードの`s`を押すことでこの本の中を検索できます。 49 | 50 | 52 | 53 | ## この本に貢献する 54 | 55 | 56 | 57 | この本はオープンソースです!タイポを見つけましたか?何か見落としがありますか?[**プルリクエストを送ってください!**][repo] (訳注: この和訳のリポジトリは[こちら][repo-ja]にあります) 58 | 59 | 61 | 62 | [Rust]: https://www.rust-lang.org 63 | [WebAssembly]: https://webassembly.org/ 64 | [trpl]: https://doc.rust-jp.rs/book/second-edition/ 65 | [mdn]: https://developer.mozilla.org/ja/docs/Learn 66 | [why-rust-wasm]: ./why-rust-and-webassembly.html 67 | [background]: ./background-and-concepts.html 68 | [tutorial]: ./game-of-life/introduction.html 69 | [reference]: ./reference/index.html 70 | [repo]: https://github.com/rustwasm/book 71 | [repo-ja]: https://github.com/moshg/rustwasm-book-ja 72 | -------------------------------------------------------------------------------- /src/reference/add-wasm-support-to-crate.md: -------------------------------------------------------------------------------- 1 | # How to Add WebAssembly Support to a General-Purpose Crate 2 | 3 | This section is for general-purpose crate authors who want to support 4 | WebAssembly. 5 | 6 | ## Maybe Your Crate Already Supports WebAssembly! 7 | 8 | Review the information about [what kinds of things can make a general-purpose 9 | crate *not* portable for WebAssembly](./which-crates-work-with-wasm.html). If 10 | your crate doesn't have any of those things, it likely already supports 11 | WebAssembly! 12 | 13 | You can always check by running `cargo build` for the WebAssembly target: 14 | 15 | ``` 16 | cargo build --target wasm32-unknown-unknown 17 | ``` 18 | 19 | If that command fails, then your crate doesn't support WebAssembly right now. If 20 | it doesn't fail, then your crate *might* support WebAssembly. You can be 100% 21 | sure that it does (and continues to do so!) by [adding tests for wasm and 22 | running those tests in CI.](#maintaining-ongoing-support-for-webassembly) 23 | 24 | ## Adding Support for WebAssembly 25 | 26 | ### Avoid Performing I/O Directly 27 | 28 | On the Web, I/O is always asynchronous, and there isn't a file system. Factor 29 | I/O out of your library, let users perform the I/O and then pass the input 30 | slices to your library instead. 31 | 32 | For example, refactor this: 33 | 34 | ```rust 35 | use std::fs; 36 | use std::path::Path; 37 | 38 | pub fn parse_thing(path: &Path) -> Result { 39 | let contents = fs::read(path)?; 40 | // ... 41 | } 42 | ``` 43 | 44 | Into this: 45 | 46 | ```rust 47 | pub fn parse_thing(contents: &[u8]) -> Result { 48 | // ... 49 | } 50 | ``` 51 | 52 | ### Add `wasm-bindgen` as a Dependency 53 | 54 | If you need to interact with the outside world (i.e. you can't have library 55 | consumers drive that interaction for you) then you'll need to add `wasm-bindgen` 56 | (and `js-sys` and `web-sys` if you need them) as a dependency for when 57 | compilation is targeting WebAssembly: 58 | 59 | ```toml 60 | [target.'cfg(target_arch = "wasm32")'.dependencies] 61 | wasm-bindgen = "0.2" 62 | js-sys = "0.3" 63 | web-sys = "0.3" 64 | ``` 65 | 66 | ### Avoid Synchronous I/O 67 | 68 | If you must perform I/O in your library, then it cannot be synchronous. There is 69 | only asynchronous I/O on the Web. Use [the `futures` 70 | crate](https://crates.io/crates/futures) and [the `wasm-bindgen-futures` 71 | crate](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/) to 72 | manage asynchronous I/O. If you're library functions are generic over some 73 | future type `F`, then that future can be implemented via `fetch` on the Web or 74 | via non-blocking I/O provided by the operating system. 75 | 76 | ```rust 77 | pub fn do_stuff(future: F) -> impl Future 78 | where 79 | F: Future, 80 | { 81 | // ... 82 | } 83 | ``` 84 | 85 | You can also define a trait and implement it for WebAssembly and the Web and 86 | also for native targets: 87 | 88 | ```rust 89 | trait ReadMyThing { 90 | type F: Future; 91 | fn read(&self) -> Self::F; 92 | } 93 | 94 | #[cfg(target_arch = "wasm32")] 95 | struct WebReadMyThing { 96 | // ... 97 | } 98 | 99 | #[cfg(target_arch = "wasm32")] 100 | impl ReadMyThing for WebReadMyThing { 101 | // ... 102 | } 103 | 104 | #[cfg(not(target_arch = "wasm32"))] 105 | struct NativeReadMyThing { 106 | // ... 107 | } 108 | 109 | #[cfg(not(target_arch = "wasm32"))] 110 | impl ReadMyThing for NativeReadMyThing { 111 | // ... 112 | } 113 | ``` 114 | 115 | ### Avoid Spawning Threads 116 | 117 | Wasm doesn't support threads yet (but [experimental work is 118 | ongoing](https://rustwasm.github.io/2018/10/24/multithreading-rust-and-wasm.html)), 119 | so attempts to spawn threads in wasm will panic. 120 | 121 | You can use `#[cfg(..)]`s to enable threaded and non-threaded code paths 122 | depending on if the target is WebAssembly or not: 123 | 124 | ```rust 125 | #![cfg(target_arch = "wasm32")] 126 | fn do_work() { 127 | // Do work with only this thread... 128 | } 129 | 130 | #![cfg(not(target_arch = "wasm32"))] 131 | fn do_work() { 132 | use std::thread; 133 | 134 | // Spread work to helper threads.... 135 | thread::spawn(|| { 136 | // ... 137 | }); 138 | } 139 | ``` 140 | 141 | Another option is to factor out thread spawning from your library and allow 142 | users to "bring their own threads" similar to factoring out file I/O and 143 | allowing users to bring their own I/O. This has the side effect of playing nice 144 | with applications that want to own their own custom thread pool. 145 | 146 | ## Maintaining Ongoing Support for WebAssembly 147 | 148 | ### Building for `wasm32-unknown-unknown` in CI 149 | 150 | Ensure that compilation doesn't fail when targeting WebAssembly by having your 151 | CI script run these commands: 152 | 153 | ``` 154 | rustup target add wasm32-unknown-unknown 155 | cargo check --target wasm32-unknown-unknown 156 | ``` 157 | 158 | For example, you can add this to your `.travis.yml` configuration for Travis CI: 159 | 160 | ```yaml 161 | 162 | matrix: 163 | include: 164 | - language: rust 165 | rust: stable 166 | name: "check wasm32 support" 167 | install: rustup target add wasm32-unknown-unknown 168 | script: cargo check --target wasm32-unknown-unknown 169 | ``` 170 | 171 | ### Testing in Node.js and Headless Browsers 172 | 173 | You can use `wasm-bindgen-test` and the `wasm-pack test` subcommand to run wasm 174 | tests in either Node.js or a headless browser. You can even integrate these 175 | tests into your CI. 176 | 177 | [Learn more about testing wasm 178 | here.](https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/index.html) 179 | -------------------------------------------------------------------------------- /src/reference/code-size.md: -------------------------------------------------------------------------------- 1 | # Shrinking `.wasm` Code Size 2 | 3 | This section will teach you how to optimize your `.wasm` build for a small code 4 | size footprint, and how to identify opportunities to change your Rust source 5 | such that less `.wasm` code is emitted. 6 | 7 | ## Why Care About Code Size? 8 | 9 | When serving a `.wasm` file over the network, the smaller it is, the faster the 10 | client can download it. Faster `.wasm` downloads lead to faster page load times, 11 | and that leads to happier users. 12 | 13 | However, it's important to remember though that code size likely isn't the 14 | end-all-be-all metric you're interested in, but rather something much more vague 15 | and hard to measure like "time to first interaction". While code size plays a 16 | large factor in this measurement (can't do anything if you don't even have all 17 | the code yet!) it's not the only factor. 18 | 19 | WebAssembly is typically served to users gzip'd so you'll want to be sure to 20 | compare differences in gzip'd size for transfer times over the wire. Also keep 21 | in mind that the WebAssembly binary format is quite amenable to gzip 22 | compression, often getting over 50% reductions in size. 23 | 24 | Furthermore, WebAssembly's binary format is optimized for very fast parsing and 25 | processing. Browsers nowadays have "baseline compilers" which parses WebAssembly 26 | and emits compiled code as fast as wasm can come in over the network. This means 27 | that [if you're using `instantiateStreaming`][hacks] the second the Web request 28 | is done the WebAssembly module is probably ready to go. JavaScript, on the other 29 | hand, can often take longer to not only parse but also get up to speed with JIT 30 | compilation and such. 31 | 32 | And finally, remember that WebAssembly is also far more optimized than 33 | JavaScript for execution speed. You'll want to be sure to measure for runtime 34 | comparisons between JavaScript and WebAssembly to factor that in to how 35 | important code size is. 36 | 37 | All this to say basically don't dismay immediately if your `.wasm` file is 38 | larger than expected! Code size may end up only being one of many factors in the 39 | end-to-end story. Comparisons between JavaScript and WebAssembly that only look 40 | at code size are missing the forest for the trees. 41 | 42 | [hacks]: https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/ 43 | 44 | ## Optimizing Builds for Code Size 45 | 46 | There are a bunch of configuration options we can use to get `rustc` to make 47 | smaller `.wasm` binaries. In some cases, we are trading longer compile times for 48 | smaller `.wasm` sizes. In other cases, we are trading runtime speed of the 49 | WebAssembly for smaller code size. We should be cognizant of the trade offs of 50 | each option, and in the cases where we trade runtime speed for code size, 51 | profile and measure to make an informed decision about whether the trade is 52 | worth it. 53 | 54 | ### Compiling with Link Time Optimizations (LTO) 55 | 56 | In `Cargo.toml`, add `lto = true` in the `[profile.release]` section: 57 | 58 | ```toml 59 | [profile.release] 60 | lto = true 61 | ``` 62 | 63 | This gives LLVM many more opportunities to inline and prune functions. Not only 64 | will it make the `.wasm` smaller, but it will also make it faster at runtime! 65 | The downside is that compilation will take longer. 66 | 67 | ### Tell LLVM to Optimize for Size Instead of Speed 68 | 69 | LLVM's optimization passes are tuned to improve speed, not size, by default. We 70 | can change the goal to code size by modifying the `[profile.release]` section in 71 | `Cargo.toml` to this: 72 | 73 | ```toml 74 | [profile.release] 75 | opt-level = 's' 76 | ``` 77 | 78 | Or, to even more aggressively optimize for size, at further potential speed 79 | costs: 80 | 81 | ```toml 82 | [profile.release] 83 | opt-level = 'z' 84 | ``` 85 | 86 | Note that, surprisingly enough, `opt-level = "s"` can sometimes result in 87 | smaller binaries than `opt-level = "z"`. Always measure! 88 | 89 | ### Use the `wasm-opt` Tool 90 | 91 | The [Binaryen][] toolkit is a collection of WebAssembly-specific compiler 92 | tools. It goes much further than LLVM's WebAssembly backend does, and using its 93 | `wasm-opt` tool to post-process a `.wasm` binary generated by LLVM can often get 94 | another 15-20% savings on code size. It will often produce runtime speed ups at 95 | the same time! 96 | 97 | ```bash 98 | # Optimize for size. 99 | wasm-opt -Os -o output.wasm input.wasm 100 | 101 | # Optimize aggressively for size. 102 | wasm-opt -Oz -o output.wasm input.wasm 103 | 104 | # Optimize for speed. 105 | wasm-opt -O -o output.wasm input.wasm 106 | 107 | # Optimize aggressively for speed. 108 | wasm-opt -O3 -o output.wasm input.wasm 109 | ``` 110 | 111 | [Binaryen]: https://github.com/WebAssembly/binaryen 112 | 113 | ### Notes about Debug Information 114 | 115 | One of the biggest contributors to wasm binary size can be debug information and 116 | the `names` section of the wasm binary. The `wasm-pack` tool, however, removes 117 | debuginfo by default. Additionally `wasm-opt` removes the `names` section by 118 | default unless `-g` is also specified. 119 | 120 | This means that if you follow the above steps you should by default not have 121 | either debuginfo or the names section in the wasm binary. If, however, you are 122 | manually otherwise preserving this debug information in the wasm binary be sure 123 | to be mindful of this! 124 | 125 | ## Size Profiling 126 | 127 | If tweaking build configurations to optimize for code size isn't resulting in a 128 | small enough `.wasm` binary, it is time to do some profiling to see where the 129 | remaining code size is coming from. 130 | 131 | > ⚡ Just like how we let time profiling guide our speed up efforts, we want to 132 | > let size profiling guide our code size shrinking efforts. Fail to do this and 133 | > you risk wasting your own time! 134 | 135 | ### The `twiggy` Code Size Profiler 136 | 137 | [`twiggy` is a code size profiler][twiggy] that supports WebAssembly as 138 | input. It analyzes a binary's call graph to answer questions like: 139 | 140 | * Why was this function included in the binary in the first place? 141 | 142 | * What is the *retained size* of this function? I.e. how much space would be 143 | saved if I removed it and all the functions that become dead code after its 144 | removal? 145 | 146 | 153 | 154 | ```text 155 | $ twiggy top -n 20 pkg/wasm_game_of_life_bg.wasm 156 | Shallow Bytes │ Shallow % │ Item 157 | ───────────────┼───────────┼──────────────────────────────────────────────────────────────────────────────────────── 158 | 9158 ┊ 19.65% ┊ "function names" subsection 159 | 3251 ┊ 6.98% ┊ dlmalloc::dlmalloc::Dlmalloc::malloc::h632d10c184fef6e8 160 | 2510 ┊ 5.39% ┊ ::fmt::he0d87479d1c208ea 161 | 1737 ┊ 3.73% ┊ data[0] 162 | 1574 ┊ 3.38% ┊ data[3] 163 | 1524 ┊ 3.27% ┊ core::fmt::Formatter::pad::h6825605b326ea2c5 164 | 1413 ┊ 3.03% ┊ std::panicking::rust_panic_with_hook::h1d3660f2e339513d 165 | 1200 ┊ 2.57% ┊ core::fmt::Formatter::pad_integral::h06996c5859a57ced 166 | 1131 ┊ 2.43% ┊ core::str::slice_error_fail::h6da90c14857ae01b 167 | 1051 ┊ 2.26% ┊ core::fmt::write::h03ff8c7a2f3a9605 168 | 931 ┊ 2.00% ┊ data[4] 169 | 864 ┊ 1.85% ┊ dlmalloc::dlmalloc::Dlmalloc::free::h27b781e3b06bdb05 170 | 841 ┊ 1.80% ┊ ::fmt::h07742d9f4a8c56f2 171 | 813 ┊ 1.74% ┊ __rust_realloc 172 | 708 ┊ 1.52% ┊ core::slice::memchr::memchr::h6243a1b2885fdb85 173 | 678 ┊ 1.45% ┊ as core::fmt::Write>::write_str::h96b72fb7457d3062 174 | 631 ┊ 1.35% ┊ universe_tick 175 | 631 ┊ 1.35% ┊ dlmalloc::dlmalloc::Dlmalloc::dispose_chunk::hae6c5c8634e575b8 176 | 514 ┊ 1.10% ┊ std::panicking::default_hook::{{closure}}::hfae0c204085471d5 177 | 503 ┊ 1.08% ┊ <&'a T as core::fmt::Debug>::fmt::hba207e4f7abaece6 178 | ``` 179 | 180 | [twiggy]: https://github.com/rustwasm/twiggy 181 | 182 | ### Manually Inspecting LLVM-IR 183 | 184 | LLVM-IR is the final intermediate representation in the compiler toolchain 185 | before LLVM generates WebAssembly. Therefore, it is very similar to the 186 | WebAssembly that is ultimately emitted. More LLVM-IR generally means more 187 | `.wasm` size, and if a function takes up 25% of the LLVM-IR, then it generally 188 | will take up 25% of the `.wasm`. While these numbers only hold in general, the 189 | LLVM-IR has crucial information that is not present in the `.wasm` (because of 190 | WebAssembly's lack of a debugging format like DWARF): which subroutines were 191 | inlined into a given function. 192 | 193 | You can generate LLVM-IR with this `cargo` command: 194 | 195 | ``` 196 | cargo rustc --release -- --emit llvm-ir 197 | ``` 198 | 199 | Then, you can use `find` to locate the `.ll` file containing the LLVM-IR in 200 | `cargo`'s `target` directory: 201 | 202 | ``` 203 | find target/release -type f -name '*.ll' 204 | ``` 205 | 206 | #### References 207 | 208 | * [LLVM Language Reference Manual](https://llvm.org/docs/LangRef.html) 209 | 210 | ## More Invasive Tools and Techniques 211 | 212 | Tweaking build configurations to get smaller `.wasm` binaries is pretty hands 213 | off. When you need to go the extra mile, however, you are prepared to use more 214 | invasive techniques, like rewriting source code to avoid bloat. What follows is 215 | a collection of get-your-hands-dirty techniques you can apply to get smaller 216 | code sizes. 217 | 218 | ### Avoid String Formatting 219 | 220 | `format!`, `to_string`, etc... can bring in a lot of code bloat. If possible, 221 | only do string formatting in debug mode, and in release mode use static strings. 222 | 223 | ### Avoid Panicking 224 | 225 | This is definitely easier said than done, but tools like `twiggy` and manually 226 | inspecting LLVM-IR can help you figure out which functions are panicking. 227 | 228 | Panics do not always appear as a `panic!()` macro invocation. They arise 229 | implicitly from many constructs, such as: 230 | 231 | * Indexing a slice panics on out of bounds indices: `my_slice[i]` 232 | 233 | * Division will panic if the divisor is zero: `dividend / divisor` 234 | 235 | * Unwrapping an `Option` or `Result`: `opt.unwrap()` or `res.unwrap()` 236 | 237 | The first two can be translated into the third. Indexing can be replaced with 238 | fallible `my_slice.get(i)` operations. Division can be replaced with 239 | `checked_div` calls. Now we only have a single case to contend with. 240 | 241 | Unwrapping an `Option` or `Result` without panicking comes in two flavors: safe 242 | and unsafe. 243 | 244 | The safe approach is to `abort` instead of panicking when encountering a `None` 245 | or an `Error`: 246 | 247 | ```rust 248 | #[inline] 249 | pub fn unwrap_abort(o: Option) -> T { 250 | use std::process; 251 | match o { 252 | Some(t) => t, 253 | None => process::abort(), 254 | } 255 | } 256 | ``` 257 | 258 | Ultimately, panics translate into aborts in `wasm32-unknown-unknown` anyways, so 259 | this gives you the same behavior but without the code bloat. 260 | 261 | Alternatively, the [`unreachable` crate][unreachable] provides an unsafe 262 | [`unchecked_unwrap` extension method][unchecked_unwrap] for `Option` and 263 | `Result` which tells the Rust compiler to *assume* that the `Option` is `Some` 264 | or the `Result` is `Ok`. It is undefined behavior what happens if that 265 | assumption does not hold. You really only want to use this unsafe approach when 266 | you 110% *know* that the assumption holds, and the compiler just isn't smart 267 | enough to see it. Even if you go down this route, you should have a debug build 268 | configuration that still does the checking, and only use unchecked operations in 269 | release builds. 270 | 271 | [unreachable]: https://crates.io/crates/unreachable 272 | [unchecked_unwrap]: https://docs.rs/unreachable/1.0.0/unreachable/trait.UncheckedOptionExt.html#tymethod.unchecked_unwrap 273 | 274 | ### Avoid Allocation or Switch to `wee_alloc` 275 | 276 | Rust's default allocator for WebAssembly is a port of `dlmalloc` to Rust. It 277 | weighs in somewhere around ten kilobytes. If you can completely avoid dynamic 278 | allocation, then you should be able to shed those ten kilobytes. 279 | 280 | Completely avoiding dynamic allocation can be very difficult. But removing 281 | allocation from hot code paths is usually much easier (and usually helps make 282 | those hot code paths faster, as well). In these cases, [replacing the default 283 | global allocator with `wee_alloc`][wee_alloc] should save you most (but not 284 | quite all) of those ten kilobytes. `wee_alloc` is an allocator designed for 285 | situations where you need *some* kind of allocator, but do not need a 286 | particularly fast allocator, and will happily trade allocation speed for smaller 287 | code size. 288 | 289 | [wee_alloc]: https://github.com/rustwasm/wee_alloc 290 | 291 | ### Use Trait Objects Instead of Generic Type Parameters 292 | 293 | When you create generic functions that use type parameters, like this: 294 | 295 | ```rust 296 | fn whatever(t: T) { ... } 297 | ``` 298 | 299 | Then `rustc` and LLVM will create a new copy of the function for each `T` type 300 | that the function is used with. This presents many opportunities for compiler 301 | optimizations based on which particular `T` each copy is working with, but these 302 | copies add up quickly in terms of code size. 303 | 304 | If you use trait objects instead of type parameters, like this: 305 | 306 | ```rust 307 | fn whatever(t: Box) { ... } 308 | // or 309 | fn whatever(t: &MyTrait) { ... } 310 | // etc... 311 | ``` 312 | 313 | Then dynamic dispatch via virtual calls is used, and only a single version of 314 | the function is emitted in the `.wasm`. The downside is the loss of the compiler 315 | optimization opportunities and the added cost of indirect, dynamically 316 | dispatched function calls. 317 | 318 | ### Use the `wasm-snip` Tool 319 | 320 | [`wasm-snip` replaces a WebAssembly function's body with an `unreachable` 321 | instruction.][snip] This is a rather heavy, blunt hammer for functions that kind 322 | of look like nails if you squint hard enough. 323 | 324 | Maybe you know that some function will never be called at runtime, but the 325 | compiler can't prove that at compile time? Snip it! Afterwards, run `wasm-opt` 326 | again with the `--dce` flag, and all the functions that the snipped function 327 | transitively called (which could also never be called at runtime) will get 328 | removed too. 329 | 330 | This tool is particularly useful for removing the panicking infrastructure, 331 | since panics ultimately translate into traps anyways. 332 | 333 | [snip]: https://github.com/fitzgen/wasm-snip 334 | -------------------------------------------------------------------------------- /src/reference/crates.md: -------------------------------------------------------------------------------- 1 | # Crates You Should Know 2 | 3 | This is a curated list of awesome crates you should know about for doing Rust 4 | and WebAssembly development. 5 | 6 | [You can also browse all the crates published to crates.io in the WebAssembly 7 | category.][wasm-category] 8 | 9 | ## Interacting with JavaScript and the DOM 10 | 11 | ### `wasm-bindgen` | [crates.io](https://crates.io/crates/wasm-bindgen) | [repository](https://github.com/rustwasm/wasm-bindgen) 12 | 13 | `wasm-bindgen` facilitates high-level interactions between Rust and 14 | JavaScript. It allows one to import JavaScript things into Rust and export Rust 15 | things to JavaScript. 16 | 17 | ### `wasm-bindgen-futures` | [crates.io](https://crates.io/crates/wasm-bindgen-futures) | [repository](https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures) 18 | 19 | `wasm-bindgen-futures` is a bridge connecting JavaSript `Promise`s and Rust 20 | `Future`s. It can convert in both directions and is useful when working with 21 | asynchronous tasks in Rust, and allows interacting with DOM events and I/O 22 | operations. 23 | 24 | ### `js-sys` | [crates.io](https://crates.io/crates/js-sys) | [repository](https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys) 25 | 26 | Raw `wasm-bindgen` imports for all the JavaScript global types and methods, such 27 | as `Object`, `Function`, `eval`, etc. These APIs are portable across all 28 | standard ECMAScript environments, not just the Web, such as Node.js. 29 | 30 | ### `web-sys` | [crates.io](https://crates.io/crates/web-sys) | [repository](https://github.com/rustwasm/wasm-bindgen/tree/master/crates/web-sys) 31 | 32 | Raw `wasm-bindgen` imports for all the Web's APIs, such as DOM manipulation, 33 | `setTimeout`, Web GL, Web Audio, etc. 34 | 35 | ## Error Reporting and Logging 36 | 37 | ### `console_error_panic_hook` | [crates.io](https://crates.io/crates/console_error_panic_hook) | [repository](https://github.com/rustwasm/console_error_panic_hook) 38 | 39 | This crate lets you debug panics on `wasm32-unknown-unknown` by providing a 40 | panic hook that forwards panic messages to `console.error`. 41 | 42 | ### `console_log` | [crates.io](https://crates.io/crates/console_log) | [repository](https://github.com/iamcodemaker/console_log) 43 | 44 | This crate provides a backend for [the `log` 45 | crate](https://crates.io/crates/log) that routes logged messages to the devtools 46 | console. 47 | 48 | ## Dynamic Allocation 49 | 50 | ### `wee_alloc` | [crates.io](https://crates.io/crates/wee_alloc) | [repository](https://github.com/rustwasm/wee_alloc) 51 | 52 | The **W**asm-**E**nabled, **E**lfin Allocator. A small (~1K uncompressed 53 | `.wasm`) allocator implementation for when code size is a greater concern than 54 | allocation performance. 55 | 56 | ## Parsing and Generating `.wasm` Binaries 57 | 58 | ### `parity-wasm` | [crates.io](https://crates.io/crates/parity-wasm) | [repository](https://github.com/paritytech/parity-wasm) 59 | 60 | Low-level WebAssembly format library for serializing, deserializing, and 61 | building `.wasm` binaries. Good support for well-known custom sections, such as 62 | the "names" section and "reloc.WHATEVER" sections. 63 | 64 | ### `wasmparser` | [crates.io](https://crates.io/crates/wasmparser) | [repository](https://github.com/yurydelendik/wasmparser.rs) 65 | 66 | A simple, event-driven library for parsing WebAssembly binary files. Provides 67 | the byte offsets of each parsed thing, which is necessary when interpreting 68 | relocs, for example. 69 | 70 | ## Interpreting and Compiling WebAssembly 71 | 72 | ### `wasmi` | [crates.io](https://crates.io/crates/wasmi) | [repository](https://github.com/paritytech/wasmi) 73 | 74 | An embeddable WebAssembly interpreter from Parity. 75 | 76 | ### `cranelift-wasm` | [crates.io](https://crates.io/crates/cranelift-wasm) | [repository](https://github.com/CraneStation/cranelift) 77 | 78 | Compile WebAssembly to the native host's machine code. Part of the Cranelift (né 79 | Cretonne) code generator project. 80 | 81 | [wasm-category]: https://crates.io/categories/wasm 82 | -------------------------------------------------------------------------------- /src/reference/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging Rust-Generated WebAssembly 2 | 3 | This section contains tips for debugging Rust-generated WebAssembly. 4 | 5 | ## Building with Debug Symbols 6 | 7 | > ⚡ When debugging, always make sure you are building with debug symbols! 8 | 9 | If you don't have debug symbols enabled, then the `"name"` custom section won't 10 | be present in the compiled `.wasm` binary, and stack traces will have function 11 | names like `wasm-function[42]` rather than the Rust name of the function, like 12 | `wasm_game_of_life::Universe::live_neighbor_count`. 13 | 14 | When using a "debug" build (aka `wasm-pack build --debug` or `cargo build`) 15 | debug symbols are enabled by default. 16 | 17 | With a "release" build, debug symbols are not enabled by default. To enable 18 | debug symbols, ensure that you `debug = true` in the `[profile.release]` section 19 | of your `Cargo.toml`: 20 | 21 | ```toml 22 | [profile.release] 23 | debug = true 24 | ``` 25 | 26 | ## Logging with the `console` APIs 27 | 28 | Logging is one of the most effective tools we have for proving and disproving 29 | hypotheses about why our programs are buggy. On the Web, [the `console.log` 30 | function](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) is the 31 | way to log messages to the browser's developer tools console. 32 | 33 | We can use [the `web-sys` crate][web-sys] to get access to the `console` logging 34 | functions: 35 | 36 | ```rust 37 | extern crate web_sys; 38 | 39 | web_sys::console::log_1(&"Hello, world!".into()); 40 | ``` 41 | 42 | Alternatively, [the `console.error` 43 | function](https://developer.mozilla.org/en-US/docs/Web/API/Console/error) has 44 | the same signature as `console.log`, but developer tools tend to also capture 45 | and display a stack trace alongside the logged message when `console.error` is 46 | used. 47 | 48 | ### References 49 | 50 | * Using `console.log` with the `web-sys` crate: 51 | * [`web_sys::console::log` takes an array of values to log](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.log.html) 52 | * [`web_sys::console::log_1` logs a single value](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.log_1.html) 53 | * [`web_sys::console::log_2` logs two values](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.log_2.html) 54 | * Etc... 55 | * Using `console.error` with the `web-sys` crate: 56 | * [`web_sys::console::error` takes an array of values to log](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.error.html) 57 | * [`web_sys::console::error_1` logs a single value](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.error_1.html) 58 | * [`web_sys::console::error_2` logs two values](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.error_2.html) 59 | * Etc... 60 | * [The `console` object on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console) 61 | * [Firefox Developer Tools — Web Console](https://developer.mozilla.org/en-US/docs/Tools/Web_Console) 62 | * [Microsoft Edge Developer Tools — Console](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide/console) 63 | * [Get Started with the Chrome DevTools Console](https://developers.google.com/web/tools/chrome-devtools/console/get-started) 64 | 65 | ## Logging Panics 66 | 67 | [The `console_error_panic_hook` crate logs unexpected panics to the developer 68 | console via `console.error`.][panic-hook] Rather than getting cryptic, 69 | difficult-to-debug `RuntimeError: unreachable executed` error messages, this 70 | gives you Rust's formatted panic message. 71 | 72 | All you need to do is install the hook by calling 73 | `console_error_panic_hook::set_once()` in an initialization function or common 74 | code path: 75 | 76 | ```rust 77 | #[wasm_bindgen] 78 | pub fn init() { 79 | console_error_panic_hook::set_once(); 80 | } 81 | ``` 82 | 83 | [panic-hook]: https://github.com/rustwasm/console_error_panic_hook 84 | 85 | ## Using a Debugger 86 | 87 | Unfortunately, the debugging story for WebAssembly is still immature. On most 88 | Unix systems, [DWARF][dwarf] is used to encode the information that a debugger 89 | needs to provide source-level inspection of a running program. There is an 90 | alternative format that encodes similar information on Windows. Currently, there 91 | is no equivalent for WebAssembly. Therefore, debuggers currently provide limited 92 | utility, and we end up stepping through raw WebAssembly instructions emitted by 93 | the compiler, rather than the Rust source text we authored. 94 | 95 | > There is a [sub-charter of the W3C WebAssembly group for 96 | > debugging][debugging-subcharter], so expect this story to improve in the 97 | > future! 98 | 99 | [debugging-subcharter]: https://github.com/WebAssembly/debugging 100 | [dwarf]: http://dwarfstd.org/ 101 | 102 | Nonetheless, debuggers are still useful for inspecting the JavaScript that 103 | interacts with our WebAssembly, and inspecting raw wasm state. 104 | 105 | ### References 106 | 107 | * [Firefox Developer Tools — Debugger](https://developer.mozilla.org/en-US/docs/Tools/Debugger) 108 | * [Microsoft Edge Developer Tools — Debugger](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide/debugger) 109 | * [Get Started with Debugging JavaScript in Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/javascript/) 110 | 111 | ## Avoid the Need to Debug WebAssembly in the First Place 112 | 113 | If the bug is specific to interactions with JavaScript or Web APIs, then [write 114 | tests with `wasm-bindgen-test`.][wbg-test] 115 | 116 | If a bug does *not* involve interaction with JavaScript or Web APIs, then try to 117 | reproduce it as a normal Rust `#[test]` function, where you can leverage your 118 | OS's mature native tooling when debugging. Use testing crates like 119 | [`quickcheck`][quickcheck] and its test case shrinkers to mechanically reduce 120 | test cases. Ultimately, you will have an easier time finding and fixing bugs if 121 | you can isolate them in a smaller test cases that don't require interacting with 122 | JavaScript. 123 | 124 | Note that in order to run native `#[test]`s without compiler and linker errors, 125 | you will need to ensure that `"rlib"` is included in the `[lib.crate-type]` 126 | array in your `Cargo.toml` file. 127 | 128 | ```toml 129 | [lib] 130 | crate-type ["cdylib", "rlib"] 131 | ``` 132 | 133 | [quickcheck]: https://crates.io/crates/quickcheck 134 | [web-sys]: https://rustwasm.github.io/wasm-bindgen/web-sys/index.html 135 | [wbg-test]: https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/index.html 136 | -------------------------------------------------------------------------------- /src/reference/deploying-to-production.md: -------------------------------------------------------------------------------- 1 | # Deploying Rust and WebAssembly to Production 2 | 3 | > **⚡ Deploying Web applications built with Rust and WebAssembly is nearly 4 | > identical to deploying any other Web application!** 5 | 6 | To deploy a Web application that uses Rust-generated WebAssembly on the client, 7 | copy the built Web application's files to your production server's file system 8 | and configure your HTTP server to make them accessible. 9 | 10 | ## Ensure that Your HTTP Server Uses the `application/wasm` MIME Type 11 | 12 | For the fastest page loads, you'll want to use [the 13 | `WebAssembly.instantiateStreaming` function][instantiateStreaming] to pipeline 14 | wasm compilation and instantiation with network transfer (or make sure your 15 | bundler is able to use that function). However, `instantiateStreaming` requires 16 | that the HTTP response has the `application/wasm` [MIME type][] set, or else it 17 | will throw an error. 18 | 19 | * [How to configure MIME types for the Apache HTTP server][apache-mime] 20 | * [How to configure MIME types for the NGINX HTTP server][nginx-mime] 21 | 22 | [instantiateStreaming]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming 23 | [MIME type]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types 24 | [apache-mime]: https://httpd.apache.org/docs/2.4/mod/mod_mime.html#addtype 25 | [nginx-mime]: https://nginx.org/en/docs/http/ngx_http_core_module.html#types 26 | 27 | ## More Resources 28 | 29 | * [Best Practices for Webpack in Production.][webpack-prod] Many Rust and 30 | WebAssembly projects use Webpack to bundle their Rust-generated WebAssembly, 31 | JavaScript, CSS, and HTML. This guide has tips for getting the most out of 32 | Webpack when deploying to production environments. 33 | * [Apache documentation.][apache] Apache is a popular HTTP server for use in 34 | production. 35 | * [NGINX documentation.][nginx] NGINX is a popular HTTP server for use in 36 | production. 37 | 38 | [webpack-prod]: https://webpack.js.org/guides/production/ 39 | [apache]: https://httpd.apache.org/docs/ 40 | [nginx]: https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/ 41 | -------------------------------------------------------------------------------- /src/reference/index.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | This section contains reference material for Rust and WebAssembly 4 | development. It is not intended to provide a narrative and be read start to 5 | finish. Instead, each subsection should stand on its own. 6 | -------------------------------------------------------------------------------- /src/reference/js-ffi.md: -------------------------------------------------------------------------------- 1 | # JavaScript Interoperation 2 | 3 | ## Importing and Exporting JS Functions 4 | 5 | ### From the Rust Side 6 | 7 | When using wasm within a JS host, importing and exporting functions from the 8 | Rust side is straightforward: it works very similarly to C. 9 | 10 | WebAssembly modules declare a sequence of imports, each with a *module name* 11 | and an *import name*. The module name for an `extern { ... }` block can be 12 | specified using [`#[link(wasm_import_module)]`][wasm_import_module], currently 13 | it defaults to "env". 14 | 15 | Exports have only a single name. In addition to any `extern` functions the 16 | WebAssembly instance's default linear memory is exported as "memory". 17 | 18 | [wasm_import_module]: https://github.com/rust-lang/rust/issues/52090 19 | 20 | ```rust 21 | // import a JS function called `foo` from the module `mod` 22 | #[link(wasm_import_module = "mod")] 23 | extern { fn foo(); } 24 | 25 | // export a Rust function called `bar` 26 | #[no_mangle] 27 | pub extern fn bar() { /* ... */ } 28 | ``` 29 | 30 | Because of wasm's limited value types, these functions must operate only on 31 | primitive numeric types. 32 | 33 | ### From the JS Side 34 | 35 | Within JS, a wasm binary turns into an ES6 module. It must be *instantiated* 36 | with linear memory and have a set of JS functions matching the expected 37 | imports. The details of instantiation are available on [MDN][instantiation]. 38 | 39 | [instantiation]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming 40 | 41 | The resulting ES6 module will contain all of the functions exported from Rust, now 42 | available as JS functions. 43 | 44 | [Here][hello world] is a very simple example of the whole setup in action. 45 | 46 | [hello world]: https://www.hellorust.com/demos/add/index.html 47 | 48 | ## Going Beyond Numerics 49 | 50 | When using wasm within JS, there is a sharp split between the wasm module's 51 | memory and the JS memory: 52 | 53 | - Each wasm module has a linear memory (described at the top of this document), 54 | which is initialized during instantiation. **JS code can freely read and write 55 | to this memory**. 56 | 57 | - By contrast, wasm code has no *direct* access to JS objects. 58 | 59 | Thus, sophisticated interop happens in two main ways: 60 | 61 | - Copying in or out binary data to the wasm memory. For example, this is one way 62 | to provide an owned `String` to the Rust side. 63 | 64 | - Setting up an explicit "heap" of JS objects which are then given 65 | "addresses". This allows wasm code to refer to JS objects indirectly (using 66 | integers), and operate on those objects by invoking imported JS functions. 67 | 68 | Fortunately, this interop story is very amenable to treatment through a generic 69 | "bindgen"-style framework: [wasm-bindgen]. The framework makes it possible to 70 | write idiomatic Rust function signatures that map to idiomatic JS functions, 71 | automatically. 72 | 73 | [wasm-bindgen]: https://github.com/rustwasm/wasm-bindgen 74 | 75 | ## Custom Sections 76 | 77 | Custom sections allow embedding named arbitrary data into a wasm module. The 78 | section data is set at compile time and is read directly from the wasm module, 79 | it cannot be modified at runtime. 80 | 81 | In Rust, custom sections are static arrays (`[T; size]`) exposed with the 82 | `#[link_section]` attribute: 83 | 84 | ```rust 85 | #[link_section = "hello"] 86 | pub static SECTION: [u8; 24] = *b"This is a custom section"; 87 | ``` 88 | 89 | This adds a custom section named `hello` to the wasm file, the rust variable 90 | name `SECTION` is arbitrary, changing it wouldn't alter the behaviour. The 91 | contents are bytes of text here but could be any arbitrary data. 92 | 93 | The custom sections can be read on the JS side using the 94 | [`WebAssembly.Module.customSections`] function, it takes a wasm Module and the 95 | section name as arguments and returns an Array of [`ArrayBuffer`]s. Multiple 96 | sections may be specified using the same name, in which case they will all 97 | appear in this array. 98 | 99 | ```js 100 | WebAssembly.compileStreaming(fetch("sections.wasm")) 101 | .then(mod => { 102 | const sections = WebAssembly.Module.customSections(mod, "hello"); 103 | 104 | const decoder = new TextDecoder(); 105 | const text = decoder.decode(sections[0]); 106 | 107 | console.log(text); // -> "This is a custom section" 108 | }); 109 | ``` 110 | 111 | [`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer 112 | [`WebAssembly.Module.customSections`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/customSections 113 | -------------------------------------------------------------------------------- /src/reference/project-templates.md: -------------------------------------------------------------------------------- 1 | # Project Templates 2 | 3 | The Rust and WebAssembly working group curates and maintains a variety of 4 | project templates to help you kickstart new projects and hit the ground running. 5 | 6 | ## `wasm-pack-template` 7 | 8 | [This template][wasm-pack-template] is for starting a Rust and WebAssembly 9 | project to be used with [`wasm-pack`][wasm-pack]. 10 | 11 | Use `cargo generate` to clone this project template: 12 | 13 | ``` 14 | cargo install cargo-generate 15 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git 16 | ``` 17 | 18 | ## `create-wasm-app` 19 | 20 | [This template][create-wasm-app] is for JavaScript projects that consume 21 | packages from npm that were created from Rust with [`wasm-pack`][wasm-pack]. 22 | 23 | Use it with `npm init`: 24 | 25 | ``` 26 | mkdir my-project 27 | cd my-project/ 28 | npm init wasm-app 29 | ``` 30 | 31 | This template is often used alongside `wasm-pack-template`, where 32 | `wasm-pack-template` projects are installed locally with `npm link`, and pulled 33 | in as a dependency for a `create-wasm-app` project. 34 | 35 | ## `rust-webpack-template` 36 | 37 | [This template][rust-webpack-template] comes pre-configured with all the 38 | boilerplate for compiling Rust to WebAssembly and hooking that directly into a 39 | Webpack build pipeline with Webpack's [`rust-loader`][rust-loader]. 40 | 41 | Use it with `npm init`: 42 | 43 | ``` 44 | mkdir my-project 45 | cd my-project/ 46 | npm init rust-webpack 47 | ``` 48 | 49 | [wasm-pack]: https://github.com/rustwasm/wasm-pack 50 | [wasm-pack-template]: https://github.com/rustwasm/wasm-pack-template 51 | [create-wasm-app]: https://github.com/rustwasm/create-wasm-app 52 | [rust-webpack-template]: https://github.com/rustwasm/rust-webpack-template 53 | [rust-loader]: https://github.com/wasm-tool/rust-loader/ 54 | -------------------------------------------------------------------------------- /src/reference/time-profiling.md: -------------------------------------------------------------------------------- 1 | # Time Profiling 2 | 3 | This section describes how to profile Web pages using Rust and WebAssembly where 4 | the goal is improving throughput or latency. 5 | 6 | > ⚡ Always make sure you are using an optimized build when profiling! `wasm-pack 7 | > build` will build with optimizations by default. 8 | 9 | ## Available Tools 10 | 11 | ### The `window.performance.now()` Timer 12 | 13 | [The `performance.now()` function][perf-now] returns a monotonic timestamp 14 | measured in milliseconds since the Web page was loaded. 15 | 16 | Calling `performance.now` has little overhead, so we can create simple, granular 17 | measurements from it without distorting the performance of the rest of the 18 | system and inflicting bias upon our measurements. 19 | 20 | We can use it to time various operations, and we can access 21 | `window.performance.now()` via [the `web-sys` crate][web-sys]: 22 | 23 | ```rust 24 | extern crate web_sys; 25 | 26 | fn now() -> f64 { 27 | web_sys::window() 28 | .expect("should have a Window") 29 | .performance() 30 | .expect("should have a Performance") 31 | .now() 32 | } 33 | ``` 34 | 35 | * [The `web_sys::window` function](https://rustwasm.github.io/wasm-bindgen/api/web_sys/fn.window.html) 36 | * [The `web_sys::Window::performance` method](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.performance) 37 | * [The `web_sys::Performance::now` method](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Performance.html#method.now) 38 | 39 | [perf-now]: https://developer.mozilla.org/en-US/docs/Web/API/Performance/now 40 | 41 | ### Developer Tools Profilers 42 | 43 | All Web browsers' built-in developer tools include a profiler. These profilers 44 | display which functions are taking the most time with the usual kinds of 45 | visualizations like call trees and flame graphs. 46 | 47 | If you [build with debug symbols][symbols] so that the "name" custom section is 48 | included in the wasm binary, then these profilers should display the Rust 49 | function names instead of something opaque like `wasm-function[123]`. 50 | 51 | Note that these profilers *won't* show inlined functions, and since Rust and 52 | LLVM rely on inlining so heavily, the results might still end up a bit 53 | perplexing. 54 | 55 | [symbols]: ./debugging.html#building-with-debug-symbols 56 | 57 | [![Screenshot of profiler with Rust symbols](../images/game-of-life/profiler-with-rust-names.png)](../images/game-of-life/profiler-with-rust-names.png) 58 | 59 | #### Resources 60 | 61 | * [Firefox Developer Tools — Performance](https://developer.mozilla.org/en-US/docs/Tools/Performance) 62 | * [Microsoft Edge Developer Tools — Performance](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide/performance) 63 | * [Chrome DevTools JavaScript Profiler](https://developers.google.com/web/tools/chrome-devtools/rendering-tools/js-execution) 64 | 65 | ### The `console.time` and `console.timeEnd` Functions 66 | 67 | [The `console.time` and `console.timeEnd` functions][console-time] allow you to 68 | log the timing of named operations to the browser's developer tools console. You 69 | call `console.time("some operation")` when the operation begins, and call 70 | `console.timeEnd("some operation")` when it finishes. The string label naming 71 | the operation is optional. 72 | 73 | You can use these functions directly via [the `web-sys` crate][web-sys]: 74 | 75 | * [`web_sys::console::time_with_label("some 76 | operation")`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.time_with_label.html) 77 | * [`web_sys::console::time_end_with_label("some 78 | operation")`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/console/fn.time_end_with_label.html) 79 | 80 | Here is a screenshot of `console.time` logs in the browser's console: 81 | 82 | [![Screenshot of console.time logs](../images/game-of-life/console-time.png)](../images/game-of-life/console-time.png) 83 | 84 | Additionally, `console.time` and `console.timeEnd` logs will show up in your 85 | browser's profiler's "timeline" or "waterfall" view: 86 | 87 | [![Screenshot of console.time logs](../images/game-of-life/console-time-in-profiler.png)](../images/game-of-life/console-time-in-profiler.png) 88 | 89 | [console-time]: https://developer.mozilla.org/en-US/docs/Web/API/Console/time 90 | 91 | ### Using `#[bench]` with Native Code 92 | 93 | The same way we can often leverage our operating system's native code debugging 94 | tools by writing `#[test]`s rather than debugging on the Web, we can leverage 95 | our operating system's native code profiling tools by writing `#[bench]` 96 | functions. 97 | 98 | Write your benchmarks in the `benches` subdirectory of your crate. Make sure 99 | that your `crate-type` includes `"rlib"` or else the bench binaries won't be 100 | able to link your main lib. 101 | 102 | However! Make sure that you know the bottleneck is in the WebAssembly before 103 | investing much energy in native code profiling! Use your browser's profiler to 104 | confirm this, or else you risk wasting your time optimizing code that isn't hot. 105 | 106 | #### Resources 107 | 108 | * [Using the `perf` profiler on Linux](http://www.brendangregg.com/perf.html) 109 | * [Using the Instruments.app profiler on macOS](https://help.apple.com/instruments/mac/current/) 110 | * [The VTune profiler supports Windows and Linux](https://software.intel.com/en-us/vtune) 111 | 112 | [web-sys]: https://rustwasm.github.io/wasm-bindgen/web-sys/index.html 113 | -------------------------------------------------------------------------------- /src/reference/tools.md: -------------------------------------------------------------------------------- 1 | # Tools You Should Know 2 | 3 | This is a curated list of awesome tools you should know about when doing Rust 4 | and WebAssembly development. 5 | 6 | ## Development, Build, and Workflow Orchestration 7 | 8 | ### `wasm-pack` | [repository](https://github.com/rustwasm/wasm-pack) 9 | 10 | `wasm-pack` seeks to be a one-stop shop for building and working with Rust- 11 | generated WebAssembly that you would like to interoperate with JavaScript, on 12 | the Web or with Node.js. `wasm-pack` helps you build and publish Rust-generated 13 | WebAssembly to the npm registry to be used alongside any other JavaScript 14 | package in workflows that you already use. 15 | 16 | ## Optimizing and Manipulating `.wasm` Binaries 17 | 18 | ### `wasm-opt` | [repository](https://github.com/WebAssembly/binaryen) 19 | 20 | The `wasm-opt` tool reads WebAssembly as input, runs transformation, 21 | optimization, and/or instrumentation passes on it, and then emits the 22 | transformed WebAssembly as output. Running it on the `.wasm` binaries produced 23 | by LLVM by way of `rustc` will usually create `.wasm` binaries that are both 24 | smaller and execute faster. This tool is a part of the `binaryen` project. 25 | 26 | ### `wasm2js` | [repository](https://github.com/WebAssembly/binaryen) 27 | 28 | The `wasm2js` tool compiles WebAssembly into "almost asm.js". This is great for 29 | supporting browsers that don't have a WebAssembly implementation, such as 30 | Internet Explorer 11. This tool is a part of the `binaryen` project. 31 | 32 | ### `wasm-gc` | [repository](https://github.com/alexcrichton/wasm-gc) 33 | 34 | A small tool to garbage collect a WebAssembly module and remove all unneeded 35 | exports, imports, functions, etc. This is effectively a `--gc-sections` linker 36 | flag for WebAssembly. 37 | 38 | You don't usually need to use this tool yourself because of two reasons: 39 | 40 | 1. `rustc` now has a new enough version of `lld` that it supports the 41 | `--gc-sections` flag for WebAssembly. This is automatically enabled for LTO 42 | builds. 43 | 2. The `wasm-bindgen` CLI tool runs `wasm-gc` for you automatically. 44 | 45 | ### `wasm-snip` | [repository](https://github.com/rustwasm/wasm-snip) 46 | 47 | `wasm-snip` replaces a WebAssembly function's body with an `unreachable` 48 | instruction. 49 | 50 | Maybe you know that some function will never be called at runtime, but the 51 | compiler can't prove that at compile time? Snip it! Then run `wasm-gc` again and 52 | all the functions it transitively called (which could also never be called at 53 | runtime) will get removed too. 54 | 55 | This is useful for forcibly removing Rust's panicking infrastructure in 56 | non-debug production builds. 57 | 58 | ## Inspecting `.wasm` Binaries 59 | 60 | ### `twiggy` | [repository](https://github.com/rustwasm/twiggy) 61 | 62 | `twiggy` is a code size profiler for `.wasm` binaries. It analyzes a binary's 63 | call graph to answer questions like: 64 | 65 | * Why was this function included in the binary in the first place? I.e. which 66 | exported functions are transitively calling it? 67 | * What is the retained size of this function? I.e. how much space would be saved 68 | if I removed it and all the functions that become dead code after its removal. 69 | 70 | Use `twiggy` to make your binaries slim! 71 | 72 | ### `wasm-objdump` | [repository](https://github.com/WebAssembly/wabt) 73 | 74 | Print low-level details about a `.wasm` binary and each of its sections. Also 75 | supports disassembling into the WAT text format. It's like `objdump` but for 76 | WebAssembly. This is a part of the WABT project. 77 | 78 | ### `wasm-nm` | [repository](https://github.com/fitzgen/wasm-nm) 79 | 80 | List the imported, exported, and private function symbols defined within a 81 | `.wasm` binary. It's like `nm` but for WebAssembly. 82 | -------------------------------------------------------------------------------- /src/reference/which-crates-work-with-wasm.md: -------------------------------------------------------------------------------- 1 | # Which Crates Will Work Off-the-Shelf with WebAssembly? 2 | 3 | It is easiest to list the things that do *not* currently work with WebAssembly; 4 | crates which avoid these things tend to be portable to WebAssembly and usually 5 | *Just Work*. A good rule of thumb is that if a crate supports embedded and 6 | `#![no_std]` usage, it probably also supports WebAssembly. 7 | 8 | ## Things a Crate Might do that Won't Work with WebAssembly 9 | 10 | ### C and System Library Dependencies 11 | 12 | There are no system libraries in wasm, so any crate that tries to bind to a 13 | system library won't work. 14 | 15 | Using C libraries will also probably fail to work, since wasm doesn't have a 16 | stable ABI for cross-language communication, and cross-language linking for wasm 17 | is very finicky. Everyone wants this to work eventually, especially since 18 | `clang` is shipping their `wasm32` target by default now, but the story isn't 19 | quite there yet. 20 | 21 | ### File I/O 22 | 23 | WebAssembly does not have access to a file system, so crates that assume the 24 | existence of a file system — and don't have wasm-specific workarounds 25 | — will not work. 26 | 27 | ### Spawning Threads 28 | 29 | There are [plans to add threading to WebAssembly][wasm-threading], but it isn't 30 | shipping yet. Attempts to spawn on a thread on the `wasm32-unknown-unknown` 31 | target will panic, which triggers a wasm trap. 32 | 33 | [wasm-threading]: https://rustwasm.github.io/2018/10/24/multithreading-rust-and-wasm.html 34 | 35 | ## So Which General Purpose Crates Tend to Work Off-the-Shelf with WebAssembly? 36 | 37 | ### Algorithms and Data Structures 38 | 39 | Crates that provide the implementation of a particular 40 | [algorithm](https://crates.io/categories/algorithms) or [data 41 | structure](https://crates.io/categories/data-structures), for example A* graph 42 | search or splay trees, tend to work well with WebAssembly. 43 | 44 | ### `#![no_std]` 45 | 46 | [Crates that do not rely on the standard 47 | library](https://crates.io/categories/no-std) tend to work well with 48 | WebAssembly. 49 | 50 | ### Parsers 51 | 52 | [Parsers](https://crates.io/categories/parser-implementations) — so long 53 | as they just take input and don't perform their own I/O — tend to work 54 | well with WebAssembly. 55 | 56 | ### Text Processing 57 | 58 | [Crates that deal with the complexities of human language when expressed in 59 | textual form](https://crates.io/categories/text-processing) tend to work well 60 | with WebAssembly. 61 | 62 | ### Rust Patterns 63 | 64 | [Shared solutions for particular situations specific to programming in 65 | Rust](https://crates.io/categories/rust-patterns) tend to work well with WebAssembly. 66 | -------------------------------------------------------------------------------- /src/setup.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | If you want to be able to use Rust for wasm then you need an environment to be able to do that! If 4 | you haven't already you'll need to install [rustup][rustup] (the official tool) in order to install 5 | and manage different versions of the Rust compiler. Follow the instructions on the site to get it 6 | installed on your machine. For the time being, you'll need Rust nightly when working with wasm: 7 | 8 | ```bash 9 | $ rustup default nightly 10 | ``` 11 | 12 | Once that's installed you'll need to get the `wasm32-unknown-unknown` toolchain. 13 | 14 | ```bash 15 | $ rustup target add wasm32-unknown-unknown --toolchain nightly 16 | ``` 17 | 18 | Next up if you're interested in making small wasm binaries you'll want to 19 | install the [wasm-gc][wasm-gc] tool to make smaller binaries and to work around bugs 20 | in the compiler toolchain for now: 21 | 22 | ```bash 23 | $ cargo install wasm-gc 24 | ``` 25 | 26 | And finally if you're *really* interested in making small wasm binaries you'll 27 | want to install `wasm-opt` from the [binaryen toolkit][binaryen]. 28 | 29 | [rustup]: https://www.rustup.rs/ 30 | [binaryen]: https://github.com/WebAssembly/binaryen 31 | [wasm-gc]: https://github.com/alexcrichton/wasm-gc 32 | -------------------------------------------------------------------------------- /src/what-is-webassembly.md: -------------------------------------------------------------------------------- 1 | # WebAssemblyとは? 2 | 3 | 4 | 5 | WebAssembly (wasm) とは単純な機械モデルと[広範な仕様][extensive specification]を伴った実行可能形式のことです。wasmはポータブルかつコンパクトで、ネイティブと同じかそれに近いスピードで実行されるように設計されています。 6 | 7 | 10 | 11 | プログラミング言語として、WebAssemblyは方法は異なっているが同じ構造を表現する二つのフォーマットから成ります: 12 | 13 | 15 | 16 | 1. `.wat`テキストフォーマット ("**W**eb**A**ssembly **T**ext"から`wat`と呼ばれます) は[S式][S-expressions]を使い、SchemeやClojureのようなLispファミリー言語との類似性があります。 17 | 18 | 21 | 22 | 2. `.wasm`バイナリフォーマットは低レベルでwasm仮想機械に使用されることを意図されています。概念的にはELFやMach-Oに似ています。 23 | 24 | 27 | 28 | 参考として、wat形式の階乗関数はこのようになります: 29 | 30 | 31 | 32 | ``` 33 | (module 34 | (func $fac (param f64) (result f64) 35 | get_local 0 36 | f64.const 1 37 | f64.lt 38 | if (result f64) 39 | f64.const 1 40 | else 41 | get_local 0 42 | get_local 0 43 | f64.const 1 44 | f64.sub 45 | call $fac 46 | f64.mul 47 | end) 48 | (export "fac" (func $fac))) 49 | ``` 50 | 51 | もし`wasm`ファイルがどのように見えるかに興味があるなら、[wat2wasm demo][wat2wasm demo]を上のコードに使うことができます。 52 | 53 | 55 | 56 | ## 線形メモリ 57 | 58 | 59 | 60 | WebAssemblyはとても単純な[メモリモデル][memory model]を持っています。wasmモジュールは一つの本質的にバイトのフラットな配列である「線形メモリ」にアクセスすることができます。このメモリはページサイズ (64K) の倍数で[拡張][memory can be grown]することができます。縮小することはできません。 61 | 62 | 65 | 66 | 67 | ## WebAssemblyはウェブだけのためのもの? 68 | 69 | 70 | 71 | 今のところ一般にJavaScriptとウェブのコミュニティに注目を集めていますが、wasmはホスト環境について一切の仮定をしません。それゆえ、wasmは将来様々な文脈で使用される「ポータブルな実行ファイル」形式になるというと推測するのは理にかなっています。しかし*今現在*、wasmはたいてい (ウェブ上や[Node.js]上のものを含めて) 多くの種類を持つJavaScript (JS) と関連付けられています。 72 | 73 | 79 | 80 | [memory model]: https://webassembly.github.io/spec/core/syntax/modules.html#syntax-mem 81 | [memory can be grown]: https://webassembly.github.io/spec/core/syntax/instructions.html#syntax-instr-memory 82 | [extensive specification]: https://webassembly.github.io/spec/ 83 | [value types]: https://webassembly.github.io/spec/core/syntax/types.html#value-types 84 | [Node.js]: https://nodejs.org 85 | [S-expressions]: https://ja.wikipedia.org/wiki/S%E5%BC%8F 86 | [wat2wasm demo]: https://cdn.rawgit.com/WebAssembly/wabt/aae5a4b7/demo/wat2wasm/ 87 | -------------------------------------------------------------------------------- /src/why-rust-and-webassembly.md: -------------------------------------------------------------------------------- 1 | # なぜRust and WebAssembly? 2 | 3 | 4 | 5 | ## 低レベル制御と高レベルエルゴノミクス 6 | 7 | 8 | 9 | JavaScript製ウェブアプリは信頼性のあるパフォーマンスを達成し、維持するのに苦労します。JavaScriptの動的型システムとガベージコレクションによる停止は役に立ちません。見たところ、不運にもJITにとって良いパスを外れてしまった場合、コードの小さい変更は劇的なパフォーマンスの低下を引き起こします。 10 | 11 | 15 | 16 | Rustはプログラマーに低レベルな制御と信頼性のあるパフォーマンスをもたらします。RustにはJavaScriptを悩ませる非決定的なガベージコレクションによる停止がありません。プログラマは間接や単相化やメモリレイアウトの制御ができます。 17 | 18 | 21 | 22 | ## .wasmの小ささ 23 | 24 | 25 | 26 | .wasmはネットワーク越しにダウンロードしなければならないので、コードサイズはとても重要です。Rustはランタイムを持っておらず、ガベージコレクタのような余分な肥大化したインクルードを持たないので.wasmのサイズを小さくできます。(コードサイズは) 実際に使う機能の分だけの負担です。 27 | 28 | 32 | 33 | ## すべてを書き直さ*ない* 34 | 35 | 36 | 37 | 既にあるコードベースを捨てる必要はありません。直ちに効果を得るために最もパフォーマンスに敏感なJavaScriptの関数をRustに移植するところから始められます。そしてもしそうしたいなら、そこでやめることもできます。 38 | 39 | 42 | 43 | 他とうまく協調する 44 | 45 | 46 | 47 | Rust and WebAssemblyは既にあるJavaScriptのツールと組み合わせられます。ECMScriptのモジュールがサポートされていて、npmやWebpackやGreenkeeperのような既に気に入っているツールを使い続けられます。 48 | 49 | 52 | 53 | ## 当然の快適さ 54 | 55 | 56 | 57 | Rustは開発者が当然のものと思うようになった現代的な快適さを備えています。例えば: 58 | 59 | 60 | 61 | * cargoを使った強力なパッケージマネジメント、 62 | 63 | 64 | 65 | * 表現力のある (そしてゼロコストの) 抽象化、 66 | 67 | 68 | 69 | そして歓迎的なコミュニティです!😊 70 | 71 | 72 | --------------------------------------------------------------------------------