Zum Hauptinhalt springen

WebAssembly-Entwicklung in Cursor: Rust, AssemblyScript und Browser-Integration

WebAssembly-Entwicklung in Cursor

WebAssembly (WASM) bringt nahezu native Performance in den Browser, indem es kompilierten Code neben JavaScript ausführt. Cursor, ein KI-gestützter IDE-Editor, beschleunigt die WebAssembly-Entwicklung durch das Erstellen von Rust- und AssemblyScript-Projekten, das Generieren von wasm-pack-Konfigurationen und das Optimieren von Hot Loops mit KI-Unterstützung. Dieser Leitfaden behandelt den gesamten Workflow vom Projekt-Setup bis zur Browser-Integration.

Voraussetzungen​

Stellen Sie sicher, dass Folgendes installiert ist, bevor Sie beginnen:

  • Cursor (neueste Version empfohlen) als IDE-Editor
  • Rust-Toolchain (rustup, cargo) für die Rust-zu-WASM-Kompilierung
  • Node.js 18 oder höher für das JavaScript-Tooling und AssemblyScript
  • wasm-pack zum Erstellen von Rust-WebAssembly-Paketen
  • Ein moderner Browser (Chrome, Firefox oder Edge) zum Testen
tipp

Wenn Sie die Rust-Toolchain nicht haben, kann das integrierte Terminal von Cursor sie installieren. Führen Sie den Rust-Installer aus und fügen Sie dann das WASM-Ziel mit rustup target add wasm32-unknown-unknown hinzu. Installieren Sie wasm-pack mit cargo install wasm-pack oder verwenden Sie das offizielle Installer-Skript.

Rust zu WebAssembly mit Cursor​

Rust ist die beliebteste Sprache für WebAssembly aufgrund ihrer Performance, Sicherheit und erstklassigen Tooling. Cursor hilft Ihnen beim Erstellen, Bauen und Debuggen von Rust-WASM-Modulen.

Ein Rust-WASM-Projekt erstellen​

Öffnen Sie einen neuen Ordner in Cursor und verwenden Sie den KI-Chat, um ein Projekt zu generieren:

Erstelle ein Rust-Projekt für WebAssembly, das:
- wasm-bindgen verwendet, um Rust und JavaScript zu verbinden
- Eine Funktion exportiert, die zwei Zahlen addiert
- Eine Funktion exportiert, die einen String umkehrt
- Eine Cargo.toml für cdylib konfiguriert enthält
- Eine Unit-Test-Datei hat

Cursor generiert die Standard-Rust-WASM-Struktur:

rust-wasm-app/
src/
lib.rs
utils.rs
tests/
web.rs
Cargo.toml

Die generierte Cargo.toml:

[package]
name = "rust-wasm-app"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = "0.2"
serde = { version = "1", features = ["derive"] }
serde-wasm-bindgen = "0.6"

[dev-dependencies]
wasm-bindgen-test = "0.3"

[profile.release]
opt-level = "s"
lto = true
codegen-units = 1

Die generierte src/lib.rs:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}

#[wasm_bindgen]
pub fn reverse_string(input: &str) -> String {
input.chars().rev().collect()
}

#[wasm_bindgen]
pub fn factorial(n: u32) -> u64 {
(1..=n as u64).product()
}

Bauen mit wasm-pack​

Das Terminal von Cursor macht die Ausführung von wasm-pack nahtlos. Bitten Sie die KI, ein Build-Skript zu erstellen:

Erstelle ein Build-Skript, das wasm-pack mit dem richtigen Ziel und
Ausgabeverzeichnis ausführt und dann den generierten pkg-Ordner in
einen web-Ordner kopiert.

Cursor generiert build.sh:

#!/usr/bin/env bash
set -e

echo "Building Rust to WebAssembly..."
wasm-pack build --target web --out-dir pkg

echo "Copying package to web folder..."
mkdir -p web/pkg
cp -r pkg/* web/pkg/

echo "Build complete. Open web/index.html in a browser."

Führen Sie es im Terminal von Cursor aus:

chmod +x build.sh
./build.sh

Tests für WASM schreiben​

Rust-WASM-Funktionen können mit wasm-bindgen-test getestet werden. Cursor generiert die Testdatei:

use rust_wasm_app::{add, reverse_string, factorial};
use wasm_bindgen_test::*;

#[wasm_bindgen_test]
fn test_add() {
assert_eq!(add(2, 3), 5);
assert_eq!(add(-1, 1), 0);
}

#[wasm_bindgen_test]
fn test_reverse_string() {
assert_eq!(reverse_string("cursor"), "rosruc");
assert_eq!(reverse_string(""), "");
}

#[wasm_bindgen_test]
fn test_factorial() {
assert_eq!(factorial(0), 1);
assert_eq!(factorial(5), 120);
assert_eq!(factorial(10), 3628800);
}

Führen Sie die Tests im Terminal von Cursor aus:

wasm-pack test --headless --firefox
warnung

WebAssembly-Tests werden in einer Browser-Engine ausgeführt, nicht als native Rust-Tests. Sie müssen einen Browser mit --chrome, --firefox oder --safari angeben, wenn Sie wasm-pack test ausführen. Wenn der Browser nicht installiert ist, schlagen die Tests stillschweigend mit einem Verbindungsfehler fehl. Die KI von Cursor kann helfen, diese Fehler zu diagnostizieren, wenn Sie die Fehlerausgabe einfügen.

AssemblyScript-Entwicklung in Cursor​

AssemblyScript kompiliert eine TypeScript-ähnliche Syntax direkt zu WebAssembly, was es zu einer ausgezeichneten Wahl für Entwickler macht, die bereits mit TypeScript vertraut sind. Cursor bietet starke Unterstützung für AssemblyScript, da die Syntax mit TypeScript überlappt.

Ein AssemblyScript-Projekt einrichten​

Erstellen Sie einen neuen Ordner und bitten Sie Cursor, ein AssemblyScript-Projekt zu erstellen:

Erstelle ein AssemblyScript-Projekt, das:
- Eine assembly/index.ts-Einstiegsdatei hat
- Funktionen für Bildverarbeitung exportiert (Graustufen, Invertieren)
- Eine asconfig.json für Release-Builds enthält
- Eine package.json mit der assemblyscript-Abhängigkeit hat

Cursor generiert:

assemblyscript-app/
assembly/
index.ts
image.ts
asconfig.json
package.json
index.html

Die assembly/index.ts:

export function grayscale(
pixels: Uint8Array,
width: i32,
height: i32
): Uint8Array {
const result = new Uint8Array(pixels.length);
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
const gray = <u8>((r * 77 + g * 150 + b * 29) >> 8);
result[i] = gray;
result[i + 1] = gray;
result[i + 2] = gray;
result[i + 3] = pixels[i + 3]; // Alpha erhalten
}
return result;
}

export function invert(pixels: Uint8Array): Uint8Array {
const result = new Uint8Array(pixels.length);
for (let i = 0; i < pixels.length; i += 4) {
result[i] = 255 - pixels[i];
result[i + 1] = 255 - pixels[i + 1];
result[i + 2] = 255 - pixels[i + 2];
result[i + 3] = pixels[i + 3];
}
return result;
}

Die asconfig.json:

{
"entries": ["assembly/index.ts"],
"targets": {
"release": {
"optimizeLevel": 3,
"shrinkLevel": 0,
"converge": false,
"noAssert": false
},
"debug": {
"debug": true
}
},
"options": {
"exportRuntime": true,
"exportMemory": true
}
}

AssemblyScript bauen​

Installieren Sie Abhängigkeiten und bauen Sie mit dem Terminal von Cursor:

npm install
npx asc assembly/index.ts --target release --outFile build/release.wasm
tipp

Die AssemblyScript-Syntax ist sehr nah an TypeScript, sodass der TypeScript-Sprachserver von Cursor hilfreiche Inline-Diagnosen bietet. Der Hauptunterschied ist, dass AssemblyScript explizite numerische Typen wie i32, u8 und f64 erfordert. Bitten Sie Cursor: "Konvertiere diese TypeScript-Funktion in gültiges AssemblyScript," und es wird die Typen korrekt annotieren.

Browser-Integration​

WebAssembly ist nur nützlich, wenn Sie es im Browser laden und aufrufen können. Cursor kann den JavaScript-Glue-Code für sowohl Rust- als auch AssemblyScript-Module generieren.

Ein Rust-WASM-Modul laden​

Bitten Sie Cursor, eine HTML-Seite zu erstellen, die das Rust-Paket lädt:

Erstelle eine web/index.html, die:
- Das WASM-Modul mit dem Web-Ziel lädt
- Die Funktionen add und reverse_string aufruft
- Die Ergebnisse auf der Seite anzeigt
- Fehlerbehandlung für fehlgeschlagenes Modulladen enthält

Cursor generiert web/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cursor WASM Demo</title>
</head>
<body>
<h1>WebAssembly in Cursor</h1>
<p>Add result: <span id="add-result"></span></p>
<p>Reverse result: <span id="reverse-result"></span></p>
<p>Factorial result: <span id="factorial-result"></span></p>

<script type="module">
import init, { add, reverse_string, factorial } from './pkg/rust_wasm_app.js';

async function run() {
try {
await init();
document.getElementById('add-result').textContent = add(40, 2);
document.getElementById('reverse-result').textContent = reverse_string('hello');
document.getElementById('factorial-result').textContent = factorial(10);
} catch (err) {
console.error('Failed to load WASM module:', err);
}
}

run();
</script>
</body>
</html>

Ein AssemblyScript-Modul laden​

AssemblyScript erzeugt eine eigenständige .wasm-Datei, die Sie manuell instanziieren. Cursor generiert den Loader:

async function loadAssemblyScriptModule() {
const response = await fetch('build/release.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
abort: (msg, file, line, column) => {
console.error(`Abort at ${file}:${line}:${column}: ${msg}`);
},
},
});
return instance.exports;
}

const exports = await loadAssemblyScriptModule();
const grayPixels = exports.grayscale(rgbaPixels, width, height);

Speicher zwischen JavaScript und WASM teilen​

Für performance-kritische Operationen wie Bildverarbeitung vermeiden Sie das Kopieren von Daten. Teilen Sie Speicher direkt über die lineare Speicher von WebAssembly. Cursor generiert eine Speicherbrücke:

// JavaScript-Seite
function processImageWithWasm(wasmExports, imageData) {
const { __new, __pin, __unpin, grayscale } = wasmExports;
const ptr = __new(imageData.length, 1); // 1 = Uint8Array-ID
const wasmMemory = new Uint8Array(wasmExports.memory.buffer);
wasmMemory.set(imageData, ptr);
const resultPtr = grayscale(ptr, width, height);
__pin(resultPtr);
const result = wasmMemory.slice(
resultPtr,
resultPtr + imageData.length
);
__unpin(resultPtr);
return result;
}
warnung

Der lineare Speicher von WebAssembly kann während der Allokation wachsen, was alle JavaScript-TypedArray-Ansichten invalidiert, die Sie halten. Erstellen Sie Ansichten immer aus wasmExports.memory.buffer nach jeder WASM-Funktion, die Speicher allokieren könnte. Wenn Sie zufällige Garbage-Werte in Ihrer Ausgabe sehen, ist eine veraltete Speicheransicht fast immer die Ursache.

KI-gestützte WASM-Optimierung​

Die KI von Cursor ist besonders effektiv bei der Optimierung von performance-kritischem WASM-Code, da sie über Speicherlayout, SIMD-Anweisungen und algorithmische Komplexität argumentieren kann.

Hot Loops optimieren​

Markieren Sie eine Schleife in Ihrem Rust- oder AssemblyScript-Code und fragen Sie Cursor:

Optimiere diese Schleife für WebAssembly. Verwende SIMD, wo möglich,
und minimiere Speicherallokationen innerhalb der Schleife.

Cursor schreibt eine skalare Schleife in eine vektorisierte Version mit std::simd in Rust um:

use std::simd::{f32x4, SimdFloat};

#[wasm_bindgen]
pub fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 {
let mut sum = f32x4::splat(0.0);
let chunks = a.chunks_exact(4).zip(b.chunks_exact(4));
for (a_chunk, b_chunk) in chunks {
let va = f32x4::from_slice(a_chunk);
let vb = f32x4::from_slice(b_chunk);
sum += va * vb;
}
let mut total = sum.reduce_sum();
let remainder = a.len() % 4;
for i in (a.len() - remainder)..a.len() {
total += a[i] * b[i];
}
total
}

Binärgröße reduzieren​

Große WASM-Binärdateien verlangsamen Seitenladevorgänge. Bitten Sie Cursor, Ihr Binärdatei zu analysieren:

Meine WebAssembly-Binärdatei ist 1,2 MB. Schlage Cargo.toml- und
Codeänderungen vor, um die Binärgröße für die Web-Auslieferung zu
reduzieren.

Cursor empfiehlt Profil-Anpassungen und Dead-Code-Eliminierung:

[profile.release]
opt-level = "z" # auf Größe optimieren
lto = true # Link-Time-Optimierung
codegen-units = 1 # einzelne Codegen-Einheit
panic = "abort" # keine Unwinding-Tabellen
strip = true # Symbole entfernen
// Vermeiden Sie Formatierungs- und Panicking-Code in Hot Paths
#[cfg(not(feature = "debug"))]
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
loop {}
}
tipp

Verwenden Sie wasm-opt als Post-Build-Schritt für zusätzliche Größenreduzierung. Bitten Sie Cursor: "Füge einen wasm-opt-Schritt zu meinem Build-Skript mit -Oz hinzu," und es wird den Befehl nach dem wasm-pack-Schritt einfügen. Dies reduziert die endgültige Binärdatei typischerweise um weitere 10 bis 20 Prozent.

WebAssembly in Cursor debuggen​

WASM zu debuggen ist schwieriger als JavaScript, da Fehler als rohe Traps auftreten. Cursor bietet mehrere Strategien.

Source Maps für Rust-WASM​

Aktivieren Sie Source Maps, damit Cursor und Browser-DevTools WASM-Frames auf Rust-Quellcode abbilden:

wasm-pack build --target web -- --features console_error_panic_hook

Installieren Sie in Ihrer lib.rs einen Panic-Hook, der in die Browser-Konsole schreibt:

use console_error_panic_hook::set_once as set_panic_hook;

#[wasm_bindgen(start)]
pub fn init() {
set_panic_hook();
console_log::init_with_level(log::Level::Info).ok();
}

Fügen Sie diese Abhängigkeiten zu Cargo.toml hinzu:

[dependencies]
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"

KI-gestützte Fehlerdiagnose​

Wenn ein WASM-Modul einen RuntimeError: unreachable auslöst, fügen Sie den Stack-Trace in den Chat von Cursor ein:

Mein WASM-Modul stürzt mit "RuntimeError: unreachable executed" ab,
wenn es große Arrays verarbeitet. Hier sind der Rust-Quellcode und
der JS-Loader. Finde den Bug.

Cursor lokalisiert Off-by-One-Fehler, Integer-Überläufe und nicht ausgerichtete Speicherzugriffe, die Traps verursachen.

Häufige Muster und Snippets​

Hier sind wiederverwendbare Muster, die Cursor bei Bedarf für Ihre WASM-Projekte generieren kann.

Strukturierte Daten übergeben​

Um komplexe Objekte zwischen JavaScript und Rust zu übergeben, verwenden Sie serde-wasm-bindgen:

use serde::{Serialize, Deserialize};
use serde_wasm_bindgen::{from_value, to_value};
use wasm_bindgen::prelude::*;

#[derive(Serialize, Deserialize)]
pub struct Point {
pub x: f64,
pub y: f64,
}

#[wasm_bindgen]
pub fn compute_centroid(points_js: JsValue) -> Result<JsValue, JsValue> {
let points: Vec<Point> = from_value(points_js)
.map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
if points.is_empty() {
return Err(JsValue::from_str("Cannot compute centroid of empty set"));
}
let (sum_x, sum_y) = points.iter()
.fold((0.0, 0.0), |(sx, sy), p| (sx + p.x, sy + p.y));
let centroid = Point {
x: sum_x / points.len() as f64,
y: sum_y / points.len() as f64,
};
to_value(&centroid)
.map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
}

Aufrufe aus JavaScript drosseln​

WASM-Funktionen, die in engen Animations-Loops aufgerufen werden, können den Hauptthread weiterhin blockieren. Verwenden Sie einen Web Worker, um schwere Berechnungen auszulagern. Cursor generiert das Worker-Gerüst:

// worker.js
import init, { process_image } from './pkg/rust_wasm_app.js';

self.onmessage = async (event) => {
await init();
const { pixels, width, height } = event.data;
const result = process_image(pixels, width, height);
self.postMessage({ result }, [result.buffer]);
};
// main.js
const worker = new Worker(new URL('./worker.js', import.meta.url), {
type: 'module',
});
worker.onmessage = (event) => {
drawProcessedPixels(event.data.result);
};
worker.postMessage({ pixels, width, height });
warnung

Das Übertragen von ArrayBuffers mit der Transfer-Liste ([result.buffer]) ist die einzige Möglichkeit, große Puffer zwischen dem Hauptthread und einem Worker ohne Kopieren zu übertragen. Das Vergessen der Transfer-Liste bei einem mehrere Megabyte großen Bild verursacht eine vollständige Kopie bei jeder Nachricht, was den Performance-Vorteil von WebAssembly zunichte macht.

Zusammenfassung der Best Practices​

Beachten Sie diese Prinzipien beim Erstellen von WebAssembly-Anwendungen in Cursor:

  • Wählen Sie die richtige Sprache: Rust für Performance und Sicherheit, AssemblyScript für eine TypeScript-ähnliche Lernkurve.
  • Minimieren Sie die JS-WASM-Grenze: Wenige, größere Aufrufe schlagen viele kleine.
  • Teilen Sie Speicher statt Puffer zu kopieren bei großen Daten wie Bildern und Audio.
  • Aktivieren Sie Größenoptimierung in Ihrem Release-Profil und führen Sie wasm-opt als Post-Build aus.
  • Verwenden Sie Web Worker für jede Berechnung, die länger als einige Millisekunden dauert, um die UI nicht zu blockieren.
  • Installieren Sie Panic-Hooks, damit Laufzeitfehler lesbare Meldungen anstatt opaker Traps anzeigen.

Durch die Kombination der Performance von WebAssembly mit der KI-gestützten Codegenerierung von Cursor können Sie hochperformante Browser-Module mit einem Bruchteil der üblichen Boilerplate ausliefern. Beschreiben Sie Ihre Absicht klar, lassen Sie Cursor das Projekt und den Glue-Code erstellen, und konzentrieren Sie Ihre Energie auf die Algorithmen, die zählen.