rust-wasm
Skill1 starsChanged 6 months agoMITView on GitHub
Rust WebAssembly development with wasm-bindgen, wasm-pack, web-sys, js-sys, and browser integration. Use when: - User compiles Rust to WebAssembly for web, workers, plugins, or edge runtimes - User uses wasm-bindgen, wasm-pack, web-sys, js-sys, gloo, trunk, or wasm-opt - User needs JS interop, typed bindings, memory transfer, or async browser APIs - User asks about WASM bundle size, panic hooks, console logging, or browser debugging - User integrates Rust WASM with React, Dioxus, Vite, npm packages, or web workers - User sees wasm32-unknown-unknown target errors or missing allocator/panic behavior - User needs shared memory, threads, atomics, or SIMD in WASM - User targets WASI or wasm32-wasip1 for server-side / edge runtimes
- Installs packages
3 other projects use a version of this.
Adopted by
Most recently taken up by
- po4yka/rust-skillstheir own version, first seen Nov 2, 2026
- Lu1sDV/skillsmdtheir own version, first seen Oct 13, 2026
- botirkhaltaev/botir-skillstheir own version, first seen Jul 24, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- po4yka/rust-skills★ 5same name
- botirkhaltaev/botir-skills★ 4same name
- Lu1sDV/skillsmd★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository24 other files it gives its agents.
- rust-api-designSkill
- rust-asyncSkill
- rust-axumSkill
- rust-cliSkill
- rust-cloud-nativeSkill
- rust-concurrencySkill
- rust-databaseSkill
- rust-dioxusSkill
- rust-ecosystemSkill
- rust-embeddedSkill
- rust-error-handlingSkill
- rust-fintechSkill
- rust-masterySkill
- rust-memory-optimizationSkill
- rust-mlSkill
- rust-networkingSkill
- rust-observabilitySkill
- rust-ownershipSkill
- rust-design-patternsSkill
- rust-performanceSkill
- rust-securitySkill
- rust-testingSkill
- rust-type-systemSkill
- rust-unsafeSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. 0 are common, 4 are distinctive, and 9 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
Keep the Rust/JS boundary coarse-grained.
- Distinctive: line 30
Treat browser APIs as fallible external systems.
- Very specific: line 31
Use cfg(target_family = "wasm") for platform-specific code.
- Very specific: line 32
Avoid wasm-bindgen on hot loops — pass whole buffers.
- Very specific: line 93
Use &mut [u8] for image/audio/data buffers.
- Very specific: line 117
Use serde_wasm_bindgen for complex return types, but avoid on hot paths where the serialization overhead matters.
- Very specific: line 195
Prefer explicit bindings over passing untyped JsValue everywhere.
- Very specific: line 218
Use &[u8] / Vec<u8> for zero-copy buffer sharing where possible.
- Very specific: line 328
Run wasm-opt -Oz for size-sensitive web apps.
- Distinctive: line 329
Keep exported functions stable and documented like a public API.
- Very specific: line 330
Avoid serde_wasm_bindgen on hot paths unless ergonomics beat copy cost.
- Very specific: line 331
Test core logic natively with normal Rust tests.
- Distinctive: line 332
Verify target environment: browser main thread, worker, Node, edge runtime, or WASI.
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Rust WebAssembly
Practical guide for shipping Rust code as WebAssembly: small boundaries, typed interop, predictable memory, and measurable bundle size.
Quick Navigation
- references/bindings.md - wasm-bindgen, web-sys, JS types, async interop
- references/bundling.md - wasm-pack, Trunk, Vite, size optimization, deployment
Golden Rules
- Keep the Rust/JS boundary coarse-grained.
- Pass bytes and IDs instead of many tiny objects.
- Enable panic hooks in debug builds, not production hot paths.
- Measure
.wasmsize afterwasm-opt, not before. - Treat browser APIs as fallible external systems.
- Use
cfg(target_family = "wasm")for platform-specific code. - Avoid
wasm-bindgenon hot loops — pass whole buffers.
Project Setup
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
cargo new image-core --lib# Cargo.toml
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
[dependencies.web-sys]
version = "0.3"
features = ["console", "Window", "Document", "HtmlCanvasElement"]
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"Export a Small API
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Filter {
strength: f32,
}
#[wasm_bindgen]
impl Filter {
#[wasm_bindgen(constructor)]
pub fn new(strength: f32) -> Self {
Self { strength }
}
pub fn apply_rgba(&self, pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
let gray = (chunk[0] as f32 * 0.299
+ chunk[1] as f32 * 0.587
+ chunk[2] as f32 * 0.114) as u8;
for channel in &mut chunk[..3] {
*channel = ((*channel as f32 * (1.0 - self.strength))
+ (gray as f32 * self.strength)) as u8;
}
}
}
}Use &mut [u8] for image/audio/data buffers. Avoid one exported call per pixel or row.
Returning Complex Types
use serde::{Serialize, Deserialize};
use wasm_bindgen::prelude::*;
#[derive(Serialize, Deserialize)]
pub struct AnalysisResult {
pub mean: f64,
pub median: f64,
pub std_dev: f64,
pub count: usize,
}
#[wasm_bindgen]
pub fn analyze(data: &[f64]) -> JsValue {
let mean = data.iter().sum::<f64>() / data.len() as f64;
let result = AnalysisResult { mean, median: 0.0, std_dev: 0.0, count: data.len() };
serde_wasm_bindgen::to_value(&result).unwrap()
}Use serde_wasm_bindgen for complex return types, but avoid on hot paths where the serialization overhead matters.
Accepting JS Callbacks
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn process_with_progress(
data: &[u8],
on_progress: &js_sys::Function,
) -> Result<(), JsValue> {
let total = data.len();
for (i, chunk) in data.chunks(1024).enumerate() {
// process chunk
let pct = (i as f64 / total as f64) * 100.0;
let this = JsValue::NULL;
let args = js_sys::Array::of1(&JsValue::from_f64(pct));
on_progress.call(&this, &args)?;
}
Ok(())
}For hot paths, prefer passing a channel or buffer over function callbacks to avoid per-call overhead.
Wasm Threads (Shared Memory)
# Cargo.toml additional settings for threading
[package.metadata.wasm-pack.profile.release]
wasm-opt = false # wasm-opt breaks shared memory// Enable shared memory in the module
#[wasm_bindgen]
pub fn init_pool(num_workers: usize) -> Result<(), JsValue> {
console_error_panic_hook::set_once();
// Use wasm-bindgen-rayon for thread pool
Ok(())
}Threads require SharedArrayBuffer support, COOP/COEP headers, and wasm-opt compatibility flags.
Debug Hooks
#[cfg(debug_assertions)]
#[wasm_bindgen(start)]
pub fn init_debug() {
console_error_panic_hook::set_once();
}[target.'cfg(debug_assertions)'.dependencies]
console_error_panic_hook = "0.1"JS Interop Shape
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(message: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
log(&format!("hello, {name}"));
}Prefer explicit bindings over passing untyped JsValue everywhere.
Async Browser APIs
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
#[wasm_bindgen]
pub async fn fetch_text(url: String) -> Result<String, JsValue> {
let window = web_sys::window().ok_or_else(|| JsValue::from_str("missing window"))?;
let response = JsFuture::from(window.fetch_with_str(&url)).await?;
let response: web_sys::Response = response.dyn_into()?;
let text = JsFuture::from(response.text()?).await?;
text.as_string().ok_or_else(|| JsValue::from_str("response text was not a string"))
}Memory Management
Rust WASM memory (WASM linear memory) is separate from the JS heap:
wasm_bindgencopies strings and arrays by default.- Use
&[u8]/Vec<u8>for zero-copy buffer sharing where possible. - Large structures are more efficient as serialized bytes than as many small JS objects.
- Calling
free()on JS objects (they'reJsValuewith Drop) is automatic when the Rust wrapper goes out of scope.
Size Optimization
wasm-pack build --release --target web
wasm-opt -Oz -o pkg/optimized.wasm pkg/my_crate_bg.wasm
# Check size
ls -lh pkg/optimized.wasm| Technique | Typical Savings |
opt-level = "z" | 15-25% |
| LTO + 1 codegen unit | 10-20% |
wasm-opt -Oz | 20-40% |
| Remove unused features | Variable |
wee_alloc (legacy) | Smaller binary, slower alloc |
Modern Rust uses the default allocator; wee_alloc is rarely needed with LTO.
WASM in Web Workers
// worker.rs — compiled separately as a dedicated wasm module
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn worker_entry() -> Result<(), JsValue> {
// Worker main loop — receives messages via browser postMessage
Ok(())
}Workers cannot access the DOM (web_sys::window() returns None). Use postMessage for communication.
WASI (Wasm on the Server)
rustup target add wasm32-wasip1
cargo build --target wasm32-wasip1 --release
# Run with wasmtime or wasmer
wasmtime run target/wasm32-wasip1/release/my-app.wasmWASI gives you file I/O, clocks, and stdio — closer to a native binary than browser WASM.
Testing WASM
// tests/web.rs
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn test_filter_applies() {
let filter = Filter::new(0.5);
let mut pixels = vec![100u8, 150, 200, 255, 50, 75, 100, 255];
filter.apply_rgba(&mut pixels);
assert!(pixels[0] < 100); // darkened
}wasm-pack test --firefox # or --chrome, --safari, --node
wasm-pack test --headlessAnti-Patterns
// Bad: thousands of boundary crossings.
#[wasm_bindgen]
pub fn set_pixel(x: u32, y: u32, r: u8, g: u8, b: u8) { /* ... */ }
// Good: one call over a whole buffer.
#[wasm_bindgen]
pub fn apply_image_filter(rgba: &mut [u8]) { /* ... */ }// Bad: unchecked browser globals panic in workers or tests.
let document = web_sys::window().unwrap().document().unwrap();
// Good: model environment availability as Result.
let document = web_sys::window()
.and_then(|window| window.document())
.ok_or_else(|| JsValue::from_str("document unavailable"))?;// Bad: re-serializing unchanged data on every frame.
let js_val = serde_wasm_bindgen::to_value(&my_data).unwrap();
// Good: cache the serialized form if data hasn't changed.
static CACHED: OnceLock<JsValue> = OnceLock::new();// Bad: large data copied across boundary repeatedly.
// The input array is cloned into WASM memory every frame.
// Good: allocate once in WASM, pass the pointer, reuse the buffer.
// See wasm-bindgen's support for manual memory management.Production Checklist
- Build release artifacts with
wasm-pack build --releaseor equivalent. - Run
wasm-opt -Ozfor size-sensitive web apps. - Keep exported functions stable and documented like a public API.
- Avoid
serde_wasm_bindgenon hot paths unless ergonomics beat copy cost. - Test core logic natively with normal Rust tests; test bindings with
wasm-bindgen-test. - Verify target environment: browser main thread, worker, Node, edge runtime, or WASI.
- Set
panic = "abort"in release profile. - Measure bundle size in CI and set a budget.
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

