- Start Date: 2019-01-23
- RFC PR: https://github.com/rustwasm/rfcs/pull/7
Summary
2019 is the year WebAssembly with Rust goes from "usable" to "stable, batteries-available, and production-ready."
To realize this goal, the Rust and WebAssembly domain working group will:
-
Cultivate a library ecosystem by collaborating on a modular toolkit
-
Bring multithreading to Rust-generated Wasm
-
Integrate best-in-class debugging support into our toolchain
-
Polish our toolchain and developer workflow, culminating in a 1.0 version of
wasm-pack
-
Invest in monitoring, testing, and profiling infrastructure to keep our tools and libraries snappy, stable and production-ready.
Motivation
This proposed roadmap draws upon
-
the community's blog posts in response to the working group's call for roadmap suggestions,
-
and the working group's core team's intuition and experience.
Detailed Explanation
Collaborating on a Modular Toolkit
The idea of building [high-level libraries] in a modular way that will allow others in the community to put the components together in a different way is very exciting to me. This hopefully will make the ecosystem as a whole much stronger.
In particular I’d love to see a modular effort towards implementing a virtual DOM library with JSX like syntax. There have been several efforts on this front but all have seemed relatively monolithic and “batteries included”. I hope this will change in 2019.
— Ryan Levick in Rust WebAssembly 2019
Don't create branded silos. Branding might perhaps be useful to achieve fame. But if we truly want Rust's Wasm story to succeed we should think of ways to collaborate instead of carving out territory.
— Yoshua Wuyts in Wasm 2019
In 2018, we created foundational libraries like js-sys
and
web-sys
. In 2019, we should build modular, high-level
libraries on top of them, and collect the libraries under an umbrella toolkit
crate for a holistic experience. This toolkit and its libraries will make
available all the batteries you want when targeting Wasm.
Building a greenfield Web application? Use the whole toolkit to hit the ground running. Carefully crafting a tiny Wasm module and integrating it back into an existing JavaScript project? Grab that one targeted library you need out from the toolkit and use it by itself.
-
Modular: Take or leave any individual component. Prefer interfaces over implementations.
-
Cultivate collaboration: We've already seen an ecosystem sprouting up in the Rust and WebAssembly domain, and lots of great experiments, but we haven't seen a lot of collaboration between projects. By deliberately creating a space for collaboration, we can reduce effort duplication, multiply impact, and help the ecosystem stay healthy.
Multithreading for Wasm
We must bring Rust’s fearless concurrency to the Web!
— Nick Fitzgerald in Rust and WebAssembly in 2019
Be the absolute cutting edge when it comes to WebAssembly, we should be thinking about being the first to satisfy [threads and atomics].
— richardanaya in My Rust 2019 Dream
Our toolchain already has experimental support for multithreading in
Wasm. Browsers are currently shipping SharedArrayBuffer
and
atomics (the primitives of multithreading for Wasm) behind feature flags, and
they expect to start shipping them enabled by default in 2019.
One of WebAssembly's selling points is the ability to effectively utilize available hardware. Multithreading extends that story from a single core to many. While multithreading will be literally possible for both JavaScript and any compiled-to-Wasm language, Rust's unique ownership system makes it economically realistic.
There are some technical snags (see the link above for details) that mean we
can't get Rust's standard library's std::thread::*
working on Wasm. But it is
still crucial that we have shared implementations of core multithreading
building blocks like thread pools and locks across the ecosystem. In 2019, we
should transform our experimental multithreading support into a production-ready
foundation for multithreading on Wasm, get popular crates like rayon
working
on the Web, and cash in on Rust's fearless concurrency.
Debugging
Before [debugging] is working properly (including variable inspection, which doesn't work with wasm at all right now), everything else is just toying around.
— anlumo in a comment on r/rust
Having [source maps] would be excellent for debugging.
— Yoshua Wuyts in Wasm 2019
Debugging is tricky because much of the story is out of this working group's hands, and depends on both the WebAssembly standardization bodies and the folks implementing browser developer tools instead. However, there are some concrete steps we can take to improve debugging:
-
Get
println!
,dbg!
, and friends working out of the box with Wasm. To achieve this, we will build support for the WebAssembly reference sysroot and standard system calls for Wasm that are in the standardization pipeline. -
Create the ability to compile our Rust-generated Wasm to JavaScript with source maps when debugging. Source maps are a limited debug info format for JavaScript that enable stepping through source locations in a debugger, instead of stepping through compiler-generated JavaScript code.
-
Add debugging-focused tracing and instrumentation features to our toolchain. For example, it is currently difficult to debug a JavaScript array buffer view of Wasm memory getting detached because Wasm memory was resized. We can make debugging easier by optionally instrumenting
mem.grow
instructions with logging.
In addition to that, we should work with the WebAssembly standardization bodies and browser developer tools makers, and actively participate in the WebAssembly debugging subcharter to create some movement in the debugging space. By keeping up the environmental and social pressure and lending a hand where we can, we will eventually have rich, source-level debugging for Wasm.
Toolchain and Workflow Polish
Setting up a Wasm project requires quite some boilerplate. It'd be nice if we could find ways to reduce this.
— Yoshua Wuyts in Wasm 2019
There are a few things that we intended to include in
wasm-pack
in 2018 that didn’t quite make the cut. [...] We should finish these tasks and polishwasm-pack
into a 1.0 tool.
— Nick Fitzgerald in Rust and WebAssembly in 2019
In 2019, our toolchain and workflow should be feature complete and
polished. wasm-pack
, being the entry point to our toolchain, will bear the
brunt of this work, but much of it will also be in tools that are invoked by
wasm-pack
rather than work in wasm-pack
itself.
-
Generate JavaScript API documentation from the Rust doc comments on
#[wasm_bindgen]
exports. -
Finish and implement the RFC for library crates depending on external NPM packages.
-
Finish and implement the RFC for local JavaScript snippets.
-
Integrate
cargo generate
intowasm-pack
for new project scaffolding. This would smooth the developer on ramp, by making one less tool required to get up and running. -
RFC and implementation for generating portable, universal NPM packages that work on the Web, with Node.js, and in any minimal JavaScript environment.
-
Define a philosophy for
wasm-pack
's user experience, interaction, and display. Once defined and agreed upon, we should triage eachwasm-pack
subcommand and ensure that it is consistent with our philosophy.
Given that this work is largely about plugging missing holes and improving user
experience, it is a bit of a laundry list. But that is also good sign: it means
that wasm-pack
is actually fairly close to being feature complete.
After we've finished all these tasks, we should publish a 1.0 release of
wasm-pack
.
Monitoring, Profiling, and Testing Infrastructure
The main objection I've experienced when proposing rust/wasm is compile times, but the end-to-end latency actually looks pretty competitive so far [...] Having a couple of benchmarks in CI and a graph online somewhere would go a long way towards keeping it that way.
— @jamii in an RFC comment
If I want to run the tests of a library using both libtest and wasm-bindgen-test I need to write:
# #![allow(unused_variables)] #fn main() { #[cfg_attr(not(target_arch = "wasm32"), test)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn my_test() { ... } #}
instead of just
# #![allow(unused_variables)] #fn main() { #[test]` fn my_test() { ... } #}
— @gnzlbg in an RFC comment
We should build perf.rust-lang.org-style infrastructure0 to keep an eye on
-
code size of popular and foundational wasm crates (such as those crates in our modular toolkit), and
-
our
wasm-bindgen
andwasm-pack
build times.
By continually tracking this data over time, and just at once at a particular, we will hold ourselves accountable to delivering on our promises of a lightweight toolkit and "stable, production-ready" toolchain.
0 Or perhaps integrate our monitoring into perf.rust-lang.org if it makes sense and the maintainers are willing.
That is the infrastructure story at the macro-level, but we also need to support the needs of crates within the ecosystem at the micro-level. That means continuing to invest in unit testing and profiling Rust-generated Wasm binaries. Concretely, we should
-
add benchmarking support to
wasm-bindgen-test
, and -
make
wasm-bindgen-test
future-compatible with the eRFC for custom test frameworks, paving the way forward for making regular#[test]
and#[bench]
Just Work™ with Wasm instead of requiring the use of#[wasm_bindgen_test]
instead.
Rationale, Drawbacks, and Alternatives
We choose to focus our efforts in 2019 where:
-
We — the Rust and WebAssembly working group — can build and ship features. Areas where we aren't potentially blocked by external factors, such as still-in-progress standards.
-
We can leverage advantages that are unique to Rust in the WebAssembly domain.
Things We can Build and Ship
We don't want our fate in anyone's hands but our own.
The toolkit and toolchain polish work don't involve any external entities that could slow our progress to a halt. For debugging, where the larger story involves significant consensus with external groups and standards work, we explicitly choose to focus on what we can do ourselves to improve our own debugging story. We do not set ourselves up to block on anything produced by the WebAssembly community group's debugging subcharter, and we won't wait on browser vendors to implement new Wasm debugging support in developer tools.
Of the roadmap items, the multithreading story has the most risk: our success in this domain relies on browsers enabling Wasm's multithreading primitives by default. However, this seems like a relatively safe bet, since the multithreading primitives have moved past their most experimental phase, Chrome is already shipping them enabled by default, and all other major browsers have implementations that just aren't enabled by default yet.
Leveraging Unique Advantages
We want to focus our efforts where we get the biggest effort to impact efficiency, and establish ourselves as leaders in WebAssembly in ways that no one else even has a route towards catching up.
The multithreading story is perhaps the biggest example of unique advantage: multithreading is infamously bug prone (to say the least!) and Rust's ownership system eliminates data races at compile time.
By building a modular toolkit of libraries, we bolster our ability to target the full spectrum from tiny module surgically inserted into an existing JavaScript application, to building a complete Web application in Rust. Any language that relies on a garbage collector, fat runtime, or is overly opinionated about FFI and interaction with the outside world can't reach the tiny module end of that spectrum.
The toolchain polish and debugging work have less clearly unique advantages. But both are table stakes for a good development experience, and the par for the course for these things in the Wasm domain is currently so low that we can and should stand out from the crowd.
Considered Alternative Roadmap Items
Here are a few alternative items that were considered for the roadmap, perhaps
because they were called out in #RustWasm2019
posts, but ultimately were not
included.
Pushing anyref
Integration into the Rust Language
We've already been well positioned to take advantage of host bindings and GC
reference types once they ship in Wasm via wasm-bindgen
. We could take it even
further and imagine a future where the Rust language was able to pass around
opaque references to objects in alternative memory spaces (some of which might
be GC'd) in a first class way: structs that are split across memory spaces, fat
pointers into multiple memory spaces, etc.
However, it isn't clear that pushing this all the way into the language will
bring that much utility over the existing "anyref
at the edges"
implementation that wasm-bindgen
already has.
Additionally, cashing in on this work could easily be blocked in a couple ways:
anyref
isn't shipping in any mainstream wasm engine yet, and getting this
language-level integration through the larger Rust RFC process with all of its
stakeholders would happen at a glacial pace (if it even happened!)
A Focus Only on Pure-Rust Web Applications
We prefer to answer "yes and" to pure-Rust Web applications via the modular toolkit that can service the full spectrum of tiny module to whole Web app, than to focus only on the whole Web app end of the spectrum. Our hope with the toolkit is that a rising tide will lift all boats, regardless where your project lands on that spectrum.
Additionally, full Web apps are not a unique advantage for Rust. JavaScript has been doing it for a while, and as far as Wasm goes, there are better-funded "competitors" in the space that will be able to provide a more compelling monolithic Web app development experience more quickly (via integration with tooling, existing ecosystems, or throwing money and developers at the problem). Microsoft and Blazor, Google and Go, bringing existing native applications to the Web with Emscripten, etc. We should compete where we are best positioned to do so, and monolithic Web applications is not that.
All that said, if you want to build a whole Web application with Rust-generated
Wasm and don't want to write any JavaScript at all, you should be able to do
so. In fact, you already can with #[wasm_bindgen(start)]
and the no-modules
target. We will never remove this ability, and the new toolkit will
only make developing a whole Web app easier.
Non-JavaScript and Non-Web Embeddings
While the whole of every non-Web and non-JavaScript WebAssembly embeddings looks very exciting, each embedding is a unique environment, and there is not yet a standard set of capabilities available. We don't want to block on waiting for a full set of standard capabilities to emerge, nor do we want to choose one particular embedding environment.
We do intend to support the reference sysroot work, and any follow up work that comes after it, but we will take advantage of these things on a opportunistic basis rather than making it a roadmap item.
We encourage anyone interested in non-JavaScript and non-Web embeddings to collaborate with the WebAssembly community group to push this story forward by defining standard Wasm capabilities!
Unresolved Questions
To be determined.