Rust vs C++ for frontend web (wasm) programming


Date: 2022-7-26

Several languages can now target wasm, I'll focus on Rust and C++ as these seem to have the most mature ecosystems, with C++'s Emscripten toolchain and Rust's wasm-bindgen (web-sys, js-sys etc) ecosystem. Keep in mind that both languages leverage LLVM's ability to generate wasm. Wasm itself has no direct access to the DOM, as such DOM calls pass through javascript.

Basically when using a language, you're buying into the ecosystem. You can still target Emscripten using Rust via the wasm32-unknown-emscripten target. However it would require that the LLVM version of Rust you're using and the LLVM version Emscripten is using are compatible. Similarly, you can invoke clang directly with the --target=wasm32 flag (requires wasm-ld and the std headers), and it should output wasm. However, the non-emscripten wasm ecosystem is barren!

Advantages of using C++:

add_executable(index src/main.cpp)
set_target_properties(index PROPERTIES SUFFIX .html LINK_FLAGS "-s WASM=1 -s EVAL_CTORS=2 --bind --shell-file ${CMAKE_CURRENT_LIST_DIR}/my_shell.html")

Disadvantages of using C++:

It makes targetting the DOM with Emscripten a bit of a chore:

#include <emscripten/val.h>

using emscripten::val;

int main() {
    auto doc = val::global("document");
    auto body = doc.call<val>("getElementsByTagName", val("body"))[0];
    auto btn = doc.call<val>("createElement", val("BUTTON"));
    body.call<void>("appendChild", btn);
    btn.set("textContent", "Click");
}

As you can probably guess, these DOM calls are stingly-typed and aren't checked at compile time, if you pass a wrong type or even a typo, it would error on runtime.

Advantages of using Rust:

// The above code translated to Rust
use wasm_bindgen::prelude::*;

fn main() {
    let win = web_sys::window().unwrap();
    let doc = win.document().unwrap();
    let body = doc.body().unwrap();
    let btn = doc.create_element("BUTTON").unwrap();
    body.append_child(&elem).unwrap();
    btn.set_text_content(Some("Click"));
}

Disadvantages of using Rust:

Conclusion

Both Rust and C++ can target the browser and perform DOM calls. Rust provides a better api with web-sys. Emscripten's bind api is stringly-typed so can be a chore to program against. The wasm32-unknown-unknown target is better geared for DOM calls or graphics via the canvas api, while emscripten is better geared for apps targetting OpenGL/SDL (games). As for client-side computation, both targets can be used.