Вступление: когда JavaScript перестаёт справляться
Мы привыкли, что весь интернет держится на JavaScript. Он гибок, прощает ошибки и работает везде. Но есть задачи, где даже самый оптимизированный JS-код начинает «тормозить»: обработка видео, 3D-рендеринг, сложные алгоритмы, игры. И тогда на сцену выходят WebAssembly и Rust — дуэт, который даёт веб-приложениям скорость нативного кода.
В этой статье мы разберём, что такое WebAssembly, почему Rust для Wasm — один из лучших выборов, и как написать свой первый модуль на Rust, который будет работать в браузере. Статья будет полезна как новичкам, так и тем, кто уже слышал про Wasm Rust, но не знает, с какой стороны подойти.
Что такое WebAssembly (Wasm)?
WebAssembly — это бинарный формат инструкций, который выполняется в браузере со скоростью, близкой к нативной. Проще говоря, это низкоуровневый «виртуальный процессор» для веба. Браузер загружает компактный .wasm-файл и исполняет его напрямую, минуя интерпретатор JavaScript.
Ключевые особенности Wasm:
- Высокая производительность — код выполняется почти так же быстро, как скомпилированный C++ или Rust.
- Безопасность — Wasm работает в изолированной песочнице, как и JS.
- Компактность — .wasm-файлы обычно весят в разы меньше, чем аналогичный JS-код.
- Переносимость — один и тот же модуль работает во всех современных браузерах.
Но Wasm — это не замена JavaScript. Это инструмент для задач, где JS слаб. Вы пишете критичный по скорости код на Rust, компилируете в Wasm, а JS служит «клеем», который вызывает этот модуль.
Почему именно Rust для WebAssembly?
Выбор языка для компиляции в Wasm — не принципиален. Можно использовать C, C++, Go, даже Python (через Pyodide). Но Rust для WebAssembly стал стандартом де-факто. Почему?
1. Безопасность памяти без сборщика мусора
Rust — единственный популярный системный язык, который гарантирует безопасность памяти на этапе компиляции. В отличие от C/C++, вы не получите segfault в рантайме. А отсутствие сборщика мусора (GC) означает, что у вас нет внезапных «фризов» — идеально для real-time приложений.
2. Нулевая стоимость абстракций
Вы пишете высокоуровневый код с итераторами, замыканиями и трейтами, но компилятор Rust превращает его в машинный код, который не уступает рукописному ассемблеру. Это даёт производительность C++ с удобством современных языков.
3. Экосистема wasm-pack и wasm-bindgen
Инструменты для компиляции Rust в Wasm доведены до ума. wasm-pack собирает проект, генерирует JS-обёртки и даже публикует пакет в npm. wasm-bindgen позволяет легко передавать строки, массивы и объекты между Rust и JavaScript.
4. Маленький размер выходного файла
Rust компилируется в Wasm без рантайма. Вы получаете минимальный бинарник. Даже с учётом стандартной библиотеки (через wasm-pack) размер редко превышает 50-100 КБ для реальных задач.
Как это работает: пишем первый модуль на Rust для Wasm
Давайте создадим простой модуль, который конвертирует градусы Цельсия в Фаренгейты и обратно. Это покажет вам всю цепочку: код на Rust → Wasm → вызов из JavaScript.
Шаг 1. Установка инструментов
Вам понадобится Rust (установите через rustup) и wasm-pack:
curl https://sh.rustup.rs -sSf | sh
# Установка wasm-pack
cargo install wasm-pack
Шаг 2. Создаём проект
cargo new --lib temp-converter
cd temp-converter
Откройте Cargo.toml и добавьте:
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
Флаг cdylib говорит компилятору создать динамическую библиотеку — то, что нужно для Wasm.
Шаг 3. Пишем код на Rust
В файле src/lib.rs напишем:
use wasm_bindgen::prelude::*;
/// Конвертирует Цельсий в Фаренгейты
#[wasm_bindgen]
pub fn celsius_to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
/// Конвертирует Фаренгейты в Цельсий
#[wasm_bindgen]
pub fn fahrenheit_to_celsius(f: f64) -> f64 {
(f - 32.0) * 5.0 / 9.0
}
/// Возвращает строку с приветствием — показывает работу со строками
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Привет, {}! Температура в C: {:.1}°C", name, 25.0)
}
Макрос #[wasm_bindgen] — ключевой. Он автоматически генерирует JS-обёртку для каждой функции. Rust-строки (String) и &str корректно преобразуются в JavaScript-строки.
Шаг 4. Компилируем в Wasm
wasm-pack build --target web
После выполнения в папке pkg появятся файлы: temp_converter_bg.wasm (бинарник) и temp_converter.js (JS-обёртка).
Шаг 5. Используем в HTML
Создайте index.html в корне проекта:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Rust + Wasm: Конвертер температур</title>
</head>
<body>
<h2>Конвертер температур на Rust</h2>
<p id="result"></p>
<script type="module">
import init, { celsius_to_fahrenheit, fahrenheit_to_celsius, greet } from './pkg/temp_converter.js';
async function run() {
await init(); // Загружаем Wasm-модуль
const c = 25.0;
const f = celsius_to_fahrenheit(c);
const back = fahrenheit_to_celsius(f);
document.getElementById('result').innerHTML = `
<b>${c}°C = ${f.toFixed(1)}°F</b><br>
<b>${f.toFixed(1)}°F = ${back