Using the cc
crate is the simplest and most reliable strategy for building Rust + C projects.
It has a couple of caveats for WASM, though. There is an open issue in Rust about the lack of extern C
reexports in cdylibs. Because WASM in the web must be a cdylib, this issue applies here.
The definitions of the symbols from C are compiled to WASM only if explicitly used by a Rust function, and regardless of usage they are never exported to the WASM public interface.
They cannot be renamed (or at least I didn't find a reliable way to do so). If we try to wrap it in Rust, i.e. by scoping it in a mod {}
and reexporting its name, we'll either get a compile error in native platforms or an empty definition in WASM.
The only solution I found while using the cc
crate was to rename the symbols in C to avoid name clashes, and the wrappers in Rust have the actual exported name.
This is OK for this demo, but might be a blocker for projects that depend on external C libraries outside their control.
This is the same calculator from the first crate, with a few tweaks:
add()
anddivide()
were renamed in C as_add()
and_divide()
to avoid name clashes, then wrapped in Rust with their actual names.
The concept remains the same:
_add()
is defined by C, and wrapped asadd()
by Rustsubtract()
is defined by Rustmultiply()
is defined by Rust but calls_add()
in C multiple times_divide()
is defined by C, wrapped asdivide()
by Rust, and callssubtract()
in Rust multiple times
All methods are exported to a single .wasm
file and can be called from JS.
As always, to ensure both ABIs are compatible, you must use Rust nightly and the --Z wasm_c_abi=spec"
flag. Check out build.sh
for an example.
This crate explores two build strategies:
- Strategy 1: with cargo build
- Strategy 2: with wasm-pack
The ./build.sh
script will compile with cargo and output the results to the /build
directory, then it will also call wasm-pack and output the results to the /pkg
directory.
To build this project yourself, you need llvm, clang, Rust nightly, and wasm-pack.
To see it working in your browser, use your preferred local server:
npx serve
Then, visit http://localhost:3000 and check the console.
The example uses both builds in the same page.
It's especially interesting to compare the raw WAT output from cargo with the optimized one from wasm-bindgen.
To do that, look at the cc_calculator.wat
from both build directories, /build
and /pkg
.