Reflecting on Rust and WebAssembly in 2018
To see how far we’ve come, let’s reflect on the Rust and WebAssembly story a
year ago: rustc
could emit WebAssembly binaries for you, but that was about
it. As far as communication with JavaScript went, you had to work with raw wasm
imports and exports yourself. That meant you could only pass 32- and 64-bit
integers and floats back and forth. No Rust structs, JavaScript objects,
strings, or slices could be passed back forth. And distributing your library’s
.wasm
so that other downstream projects could depend on it? Good luck.
While it was clear there was huge potential for Rust and WebAssembly, no one was sure what exactly that meant. So when the Rust and WebAssembly domain working group formed, we rallied around making this shared vision into a reality:
Compiling Rust to WebAssembly should be the best choice for fast, reliable code for the Web.
As our ideas evolved, we distilled another core value:
Rust and WebAssembly is here to augment your JavaScript, not replace it.
The same way that Rust integrates with C libraries and calling conventions on native targets, it should play nice with JavaScript and HTML5 APIs on the Web. You should not have to rewrite your whole Web application or JavaScript library. We cannot realize our vision for Rust and wasm if it means you have to start over from scratch; it wouldn’t be practical.
Given these shared values and vision, we set out goals for what we wanted the Rust and WebAssembly ecosystem, toolchain, and workflow to look like by the time Rust 2018 shipped.
Goal: ☑ Zero-Cost JavaScript Interoperation
Rust enables fast and expressive code by leveraging zero-cost abstractions. We
wanted to apply this principle to our whole JS interop infrastructure. Yes, you
can write your own boilerplate to pass DOM nodes to Rust-generated wasm, but you
shouldn’t have to, and the provided infrastructure should be as fast as if you
did hand-code it. If you call IndexedDB APIs, that shouldn’t bloat your
.wasm
binary with unused bindings to Web GL functions.
We created wasm-bindgen
as the foundation for zero-cost JavaScript
interoperation. wasm-bindgen
facillitates communication between
JavaScript and WebAssembly, and generates glue code that you would otherwise
have to write yourself. On top of wasm-bindgen
, we built js-sys
(raw
bindings to ECMAScript APIs) and web-sys
(raw bindings to Web
APIs).
Using the wasm-bindgen
ecosystem, we can easily and performantly
- export rich APIs from our Rust-generated wasm libraries, so they are callable from JavaScript, and
- import JavaScript and Web APIs into our Rust-generated wasm.
All in a zero-cost manner.
Additionally, wasm-bindgen
is forward-compatible with the WebAssembly host
bindings proposal. Host bindings will remove the tiny, generated
JavaScript shim functions that sit between our wasm functions and DOM
methods. Eventually, host bindings promises to unlock
even-faster-than-JavaScript DOM access since calls can be statically validated
once rather than dynamically checked every time.
Goal: ☑ Distributing Rust-Generated Wasm as an NPM Library
Superb integration isn’t only about exporting and importing functionality between Rust-generated WebAssembly and JavaScript. It is also fitting into the JavaScript’s distribution mechanisms, and a big chunk of that story is NPM.
We built wasm-pack
to make it easy to create and publish NPM
packages from your Rust and WebAssembly code. There didn’t used to be any story
whatsoever for sharing Rust-generated wasm modules. Now, all it takes is:
wasm-pack publish
Goal: ☑ Get Developers Productive Fast
We wrote a Rust and WebAssembly book that teaches you all the ins and outs of WebAssembly development with Rust. It features a tutorial where you build an implementation of Conway’s Game of Life, and then you learn to write tests for headless browsers, debug wasm code when things go wrong, and how to diagnose slow code paths and then speed them up.
We realized that there are a bunch of “post-build” tools you want to run after
cargo
and rustc
emit the initial .wasm
binary. For usability and developer
productivity, we expanded wasm-pack
’s role from creating and publishing NPM
packages to orchestrating all of these tasks. wasm-pack
will manage your
wasm-bindgen
CLI binaries and install browsers’ WebDriver clients for you
automatically.
For example, want to run tests in a headless Firefox browser? Just run
wasm-pack test --headless --firefox
No need to pull your hair out trying to install and configure anything!
Finally, we recognized that getting your Rust and WebAssembly project set up initially involves a bit of boilerplate and configuration that new users aren’t prepared for and experienced users don’t want to waste time on. So we created a variety of project templates for different use cases, so you can hit the ground running:
wasm-pack-template
for creating NPM libraries with Rust and Wasm.create-wasm-app
for creating Web applications built on top of Rust-generated wasm NPM libraries.rust-webpack-template
for creating whole Web applications with Rust, WebAssembly, and the Webpack bundler.rust-parcel-template
for creating whole Web applications with Rust, WebAssembly, and the Parcel bundler.
Goal: ☑ Rust-Generated Wasm Should be Testable and Debuggable
We recognized that testing and debugging infrastructure are table stakes for creating reliable code and developer productivity.
By default, wasm can’t log any panics or errors because it doesn’t have any
“syscall” or I/O functionality. You have to add imports for that sort of thing
yourself, and then instantiate the module with the appropriate functions. To
remedy this problem, and to ensure that panics are always debuggable, we created
the console_error_panic_hook
crate, which
redirects panic messages into the browser’s devtools console.
While you can always run normal #[test]
s on the native target for portable,
platform-agnostic code, that isn’t sufficient for testing your library’s
interaction with the DOM, asynchronous JavaScript promises, or event
handlers. So we created the wasm-bindgen-test
infrastructure, and made installing and configuring the
necessary binaries for headless browser and Node.js testing a breeze with
wasm-pack test
.
We also had experienced that diagnosing where code size was coming from could be
hard with WebAssembly. We wanted to know things like which function was calling
another function, and causing it to be included in the .wasm
binary, so we
created the Twiggy🌱 code size profiler for WebAssembly.
Shallow Bytes │ Shallow % │ Retaining Paths
───────────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
152 ┊ 5.40% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e
┊ ┊ ⬑ func[2]
┊ ┊ ⬑ <wee_alloc::size_classes::SizeClassAllocPolicy<'a> as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6
┊ ┊ ⬑ func[5]
┊ ┊ ⬑ elem[0]
┊ ┊ ⬑ hello
┊ ┊ ⬑ func[8]
┊ ┊ ⬑ export "hello"
#RustWasm2019
All of our goals have been focused on things we could deliver in tandem with the 2018 edition. But now that the 2018 edition has shipped, it is time to think about what we want to achieve in 2019 and beyond.
This is where you come in!
Following in the larger Rust project’s tradition, we’re asking the community to write blog posts reflecting on Rust and WebAssembly in 2018 and proposing goals and directions for Rust and WebAssembly in 2019. We’ll read everything, and then propose an RFC for the Rust and WebAssembly domain working group’s roadmap in 2019.
Write down your thoughts on whatever your writing platform of choice is. It could be:
- Your personal or company blog
- A GitHub gist
- A Medium post
- Any other platform you prefer
We’re looking for posts on many different topics:
- Ideas for community programs
- Tooling enhancements
- Ecosystem and library needs
- Documentation improvements
- Anything else related to Rust and Wasm!
Tweet your write up with the #RustWasm2019
hashtag or drop a link
on this github issue. We’ll aggregate everything
everyone has written in another big post on this blog. Then, the core Rust and
WebAssembly working group team will read over all of them and write up an RFC
for the working group’s 2019 roadmap! This RFC will follow our normal RFC
process and everyone will have a chance to discuss it, improve it, and help
polish it.
Preliminary Timeline
- Now through January 15th: Share your
#RustWasm2019
post, read posts by others, discuss them, bounce ideas back and forth. - End of January: We’ll formally propose the 2019 roadmap RFC, and then work it through the RFC process together as a community.
- End of February: We’re aiming for having consensus on the 2019 roadmap and merging the RFC before the end of February.
Thank You for a Wonderful 2018! 💖
Thanks to everyone who contributed to Rust and WebAssembly in 2018! (Apologies if we missed anyone in this list!)
- 0xazure
- Aaron Turon
- Aditya Arora
- Aidan Hobson Sayers
- Aleksey Kladov
- Alex Crichton
- Alex Macleod
- Alexander Kryvomaz
- Alfie John
- Anders Pitman
- Andrew Champion
- Andrew Chin
- Andy Bell
- Anna Bogus
- Anton Danilkin
- Ashley Williams
- Ben Merritt
- Benjamin Kampmann
- Blixt
- Bradlee Speice
- Cameron Taggart
- Camille TJHOA
- Chinedu Francis Nwafili
- Chris Goller
- Chris Kolodin
- Christian Bourjau
- Christopher Lane Hinson
- Cldfire
- Corbin Uselton
- Corey Farwell
- Craig Disselkoen
- Dan Fockler
- Dan Reeves
- Daniel Gollahon
- Daniele Esposti
- Danielle Pham
- Darren Tsung
- David Flemström
- David McNeil
- David O’Connor
- DebugSteven
- Dimitrii Nemkov
- Dmitry Kashitsyn
- Eduard Kyvenko
- Erick Tryzelaar
- Erika Kloss
- Evan Shaw
- Felix Schütt
- Florian Gilcher
- Frank Hassanabad
- Frazer McLean
- Gergely Nagy
- Guy Waldman
- Hendrik Sollich
- Henrik Sjööh
- Herman J. Radtke III
- Hidehito Yabuuchi
- Ian Duke
- Ian McIntyre
- Ingvar Stepanyan
- Ioannis Valasakis
- Ivan Enderlin
- J. Ryan Stinnett
- Jamen Marz
- Jamie Kyle
- Jan Willem Henckel
- Jan-Erik Rediger
- Jannik Keye
- Jason Davies
- Jason Wohlgemuth
- Jesper Håkansson
- Jim Blandy
- Joel Gallant
- Johann Hofmann
- Johannes Henninger
- John Lewis
- Jonas Trollvik
- Jonathan Kingston
- Jonathan Sundqvist
- Josh Triplett
- Joshua Liebow-Feeser
- Joshua Sheard
- Josmar Dias
- João Lucas Lucchetta
- Julius Rakow
- Junjie Huang
- Katharina Fey
- Kevin Hoffman
- Kirill Bulatov
- Kyle Lin
- Lachezar Lechev
- Laurentiu Nicola
- Liigo Zhuang
- LongYinan
- Luke Wagner
- Mackenzie Clark
- Mackiovello
- Manas Karekar
- Marcin Baraniecki
- Mario Reder
- Mark Andrus Roberts
- Mark Hintz
- Markus Stange
- Mason Stallmo
- Matias Insaurralde
- Matt Harrison
- Matt Howell
- Matt Kraai
- Matt Long
- MaxD / vj userZero
- MaxXor
- Michael Gattozzi
- Michael Gerhaeuser
- Michael Hoffmann
- Mirclus
- Nathan Froyd
- Nick Fitzgerald
- Nik Graf
- Nikolay Volf
- Noah Lemen
- Noumir Poutipou
- OJ Kwon
- Pascal Brandt
- Pascal Hertleif
- Pat Hickey
- Peter Trotman
- R. Andrew Ohana
- Rahul Sharma
- Ralph Giles
- Renée Kooi
- Ricardo Ambrogi
- Richard Dodd (dodj)
- Robert Masen
- Roberto Huertas
- Rongjian Zhang
- Ruben Schmidmeister
- Ryan Levick
- Sallar Kaboli
- Santiago Pastorino
- Satoshi Amemiya
- Scott Johnson
- Sebastian Köln
- Sendil Kumar N
- Sergey Pepyakin
- Sharad Chand
- Sonny Scroggin
- Sophie Alpert
- Spencer Wilson
- Stefan Novak
- Stefan Zimmermann
- Stephan Renatus
- Stephan Wolski
- Steve Klabnik
- Sven Sauleau
- T. Nagasawa
- Tao He
- Ted Mielczarek
- Theemathas Chirananthavat
- Thiago Pontes
- Thomas Eizinger
- Tim Ryan
- Tobias Bieniek
- Tomohide Takao
- Tomáš Hübelbauer
- Tyler Laing
- Tyler Wilcock
- William Lundstedt
- YUyz
- Yoshua Wuyts
- Yury Delendik
- Yuval Kohavi
- Zachary Pierce
- Zack Pierce
- afdw
- alkahest
- andy-bell
- arjunyel
- ashley williams
- belfz
- bokuweb
- bspeice
- csmoe
- data-pup
- dependabot[bot]
- frankhassanbad
- gaurikholkar
- gnzlbg
- huangjj27
- janczer
- johnthagen
- kohensu
- konstin
- kryptan
- kzvi
- limira
- na-g
- pup
- robert masen
- robertdurst
- sarahmeyer
- sepiropht
- sigmaSd
- soryrawyer
- teovoinea
- toversus
- twilco
- xeqlol