I have a large application written in Rust I'd like to port to run on the web.
Everywhere I turn, I'm running into obstacles. So I wanted to lay out my options to confirm what I'm getting myself into. Here is what I have so far, do I have everything right? Missing anything?
Option 1: Emscripten, target wasm32-unknown-emscripten. This seems closest to what I want, as Emscripten supports mapping file I/O to a virtual web filesystem, OpenGL to WebGL, SDL and glfw to HTML5 APIs, and so on allowing native applications to be ported without large-scale rewrites.
Unfortunately, the Rust community seems to have moved on and Emscripten support has fallen by the wayside.
To create and manage windows, the cross-platform winit crate is popular. It has/had some form of Emscripten support, but it broke with newer versions, and older versions break too. I started fixing the incompatibilities, but once one bug is fixed, more arise I don't yet understand.
To get this working, significant investment would be required to get the Emscripten backend and crates up to shape. The community just isn't there, so I would be mostly on my own here.
Option 2: wasm32-unknown-unknown target. A newer lightweight target without Emscripten, this is what the Rust and WebAssembly book from the Rustwasm group recommends, along with the wasm-pack tool.
This is what the community seems to have moved onto, so it has greater support going for it. However, it is more limited in that it doesn't implement most of the standard library or translate other native APIs to their web-based equivalents.
I actually got my project to compile to this target, but it crashes early on in std::fs
, trying to open a file isn't supported so it panics operation not supported on wasm yet
. libstd/sys/wasm shows the missing implementation:
impl File {
pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
unsupported()
}
Should I fork the compiler and fill in a usable implementation here? Probably not a viable idea.
My other idea of how to workaround this is to conditionalize the imports based on the targets, something like:
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use ???:fs;
} else {
use std::fs;
}
}
The problem then becomes how to implement the web-based std::fs
. Has anyone done this yet?
Searching for crates, found the deceptively-named stdweb. But this is more of a new standard library rethought for the web, not std
for the web. stdweb::web::File
isn't compatible with std::fs::File
, but instead refers to the JavaScript File interface.
So if I was to go down this path, I'd have to reimplement any missing std
modules I need (unless someone already has).
As for windowing, winit has open pull requests for a stdweb backend and wasm_bindgen, both dependent on a major refactor coming up. Neither of these changes are complete, but there is interest so I am optimistic it will work well going forward. I can wait, but still would have to solve the std::fs
(etc.) problem.
Option 3: wasm32-wasi, the newest WebAssembly backend for Rust, implements the WebAssembly System Interface instead of leaving it "unknown". The WASI intro and WASI tutorial explain what this is about, and they have an example of a Rust program like this:
use std::env;
use std::fs;
use std::io::{Read, Write};
...
let mut input_file =
fs::File::open(input_fname)
This looks great, std::fs works!
How so? The wasm32-wasi target compiles to a standard set of WASI API calls, which is in turn implemented by the runtime: the native runtime wasmtime, or a browser polyfill. Pretty cool.
The downside is the wasm32-wasi target is nightly-only, and wasm-pack doesn't currently support it. WASI is also still only a MVP so it is somewhat limited, it is not clear how or if winit would be able to support it. POSIX/Unix-like APIs for sure, though.
The capabilities-based architecture when executed on a native platform is also an interesting benefit, though not strictly necessary for my purposes.
Option 4: something else Are there any other options? Rewriting in C/C++ and using Emscripten directly would solve all of these problems, but is a nonstarter since I want to use Rust.
Conclusions
So what should I do? All of the choices have pros and cons:
- wasm32-unknown-emscripten: broken support difficult to fix, maintenance, lack of support, heavyweight
- wasm32-unknown-unknown: stronger community, but more busywork reimplementing web backends emscripten already did
- wasm32-wasi: works with the filesystem, polyfills for the browser, but too new for much tool/crate support
I'm leaning towards #2, with an eye towards #3, essentially backporting the missing functionality from #1. How does this plan sound to everyone, am I missing anything, has anyone else went down this path and have any advice how to proceed?