О нас Руководства Проекты Контакты
Админка
пожалуйста подождите

WebAssembly (Wasm) позволяет запускать код Rust в браузерах со скоростью, близкой к нативной. Это открывает возможности для вычислительно интенсивных задач — таких как игры, обработка изображений и криптография, — с которыми JavaScript не справляется эффективно. В этом руководстве рассматривается сборка модулей Wasm на Rust с точки зрения senior-разработчика.

Почему Rust + WebAssembly

Эта комбинация превосходна, потому что:

  1. Производительность: скорость, близкая к нативной, в браузерах
  2. Безопасность: гарантии Rust сохраняются и в Wasm
  3. Размер: бинарные файлы меньше, чем у C/C++
  4. Interop: бесшовная интеграция с JavaScript
  5. Портативность: работает везде, где работает Wasm

Сценарии использования: игры, обработка аудио/видео, криптография, симуляции, парсеры и любая CPU-интенсивная работа.

Настройка

Установите Rust

# Установите Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Добавьте target Wasm
rustup target add wasm32-unknown-unknown
# Установите wasm-pack (рекомендуется)
cargo install wasm-pack
# Установите wasm-bindgen-cli (для ручных сборок)
cargo install wasm-bindgen-cli

Структура проекта

# Создайте новый проект
cargo new --lib my-wasm
cd my-wasm
my-wasm/
├── Cargo.toml
├── src/
│ └── lib.rs
├── pkg/ # Сгенерировано wasm-pack
│ ├── my_wasm.js
│ ├── my_wasm_bg.wasm
│ └── package.json
└── www/ # Опционально: веб-демо
└── index.html

Конфигурация

Cargo.toml

[package]
name = "my-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3" # Типы JavaScript
web-sys = "0.3" # Web API
# Для отладки через console.log
console_error_panic_hook = "0.1"
[dev-dependencies]
wasm-bindgen-test = "0.3"
[profile.release]
# Оптимизация под размер
opt-level = "s"
lto = true

Базовый пример

Простая функция

src/lib.rs:

use wasm_bindgen::prelude::*;
// Вызывается при инстанцировании wasm-модуля
#[wasm_bindgen(start)]
pub fn main() {
// Установите panic hook для более понятных сообщений об ошибках
console_error_panic_hook::set_once();
}
// Экспортируйте простую функцию
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
// Экспортируйте функцию, которая принимает и возвращает числа
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Экспортируйте функцию, которая работает с массивами
#[wasm_bindgen]
pub fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}

Сборка

# Сборка для web (рекомендуется)
wasm-pack build --target web
# Сборка для bundler’ов (webpack и т. п.)
wasm-pack build --target bundler
# Сборка для Node.js
wasm-pack build --target nodejs
# Release-сборка с оптимизациями
wasm-pack build --target web --release

Использование в HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Rust Wasm Demo</title>
</head>
<body>
<h1>Rust WebAssembly</h1>
<div id="output"></div>
<script type="module">
import init, { greet, add, sum } from './pkg/my_wasm.js';
async function run() {
// Initialize the Wasm module
await init();
// Call Rust functions
const greeting = greet("World");
console.log(greeting); // "Hello, World!"
const result = add(5, 3);
console.log("5 + 3 =", result); // 8
const total = sum(new Int32Array([1, 2, 3, 4, 5]));
console.log("Sum:", total); // 15
document.getElementById('output').textContent = greeting;
}
run();
</script>
</body>
</html>

Interop с JavaScript

Вызов JavaScript из Rust

use wasm_bindgen::prelude::*;
// Импортируйте функции JavaScript
#[wasm_bindgen]
extern "C" {
// console.log
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
// alert
fn alert(s: &str);
// Пользовательская функция JavaScript
#[wasm_bindgen(js_namespace = window)]
fn customCallback(value: i32);
}
#[wasm_bindgen]
pub fn rust_function() {
log("Called from Rust!");
alert("Hello from Rust!");
}

Работа с DOM

Включите функции web-sys в Cargo.toml:

[dependencies.web-sys]
version = "0.3"
features = [
"Document",
"Element",
"HtmlElement",
"Window",
"console",
]
use wasm_bindgen::prelude::*;
use web_sys::{Document, Element, Window};
#[wasm_bindgen]
pub fn manipulate_dom() -> Result<(), JsValue> {
let window: Window = web_sys::window().expect("no window");
let document: Document = window.document().expect("no document");
// Создайте элемент
let element: Element = document.create_element("div")?;
element.set_inner_html("<h1>Created by Rust!</h1>");
element.set_attribute("class", "rust-created")?;
// Добавьте в body
let body = document.body().expect("no body");
body.append_child(&element)?;
Ok(())
}

Callback’и и замыкания

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn set_timeout_rust(callback: &js_sys::Function, delay: i32) {
let window = web_sys::window().expect("no window");
window.set_timeout_with_callback_and_timeout_and_arguments_0(
callback,
delay
).unwrap();
}
// Замыкание Rust в JavaScript
#[wasm_bindgen]
pub fn create_callback() -> JsValue {
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
web_sys::console::log_1(&format!("Clicked at: {}, {}", event.client_x(), event.client_y()).into());
}) as Box<dyn FnMut(_)>);
let js_value = closure.as_ref().clone();
closure.forget(); // Предотвратите освобождение замыкания Rust
js_value
}

Struct’ы и классы

Экспорт Struct как класса

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Counter {
value: i32,
}
#[wasm_bindgen]
impl Counter {
#[wasm_bindgen(constructor)]
pub fn new(initial: i32) -> Counter {
Counter { value: initial }
}
pub fn increment(&mut self) {
self.value += 1;
}
pub fn decrement(&mut self) {
self.value -= 1;
}
pub fn get(&self) -> i32 {
self.value
}
pub fn set(&mut self, value: i32) {
self.value = value;
}
}

Использование в JavaScript:

import init, { Counter } from './pkg/my_wasm.js';
await init();
const counter = new Counter(10);
console.log(counter.get()); // 10
counter.increment();
counter.increment();
console.log(counter.get()); // 12
counter.free(); // Очистите память Wasm

Практический пример: обработка изображений

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn grayscale(data: &mut [u8]) {
// Обработайте данные пикселей RGBA
for pixel in data.chunks_exact_mut(4) {
let r = pixel[0] as f32;
let g = pixel[1] as f32;
let b = pixel[2] as f32;
// Метод яркости (luminosity)
let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
pixel[0] = gray;
pixel[1] = gray;
pixel[2] = gray;
// pixel[3] — это alpha, оставьте без изменений
}
}
#[wasm_bindgen]
pub fn invert(data: &mut [u8]) {
for pixel in data.chunks_exact_mut(4) {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
}
}
#[wasm_bindgen]
pub fn brightness(data: &mut [u8], adjustment: i32) {
for pixel in data.chunks_exact_mut(4) {
pixel[0] = ((pixel[0] as i32 + adjustment).clamp(0, 255)) as u8;
pixel[1] = ((pixel[1] as i32 + adjustment).clamp(0, 255)) as u8;
pixel[2] = ((pixel[2] as i32 + adjustment).clamp(0, 255)) as u8;
}
}

Интеграция с JavaScript:

import init, { grayscale } from './pkg/my_wasm.js';
async function processImage() {
await init();
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Получите данные изображения
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Обработайте с помощью Rust
grayscale(imageData.data);
// Запишите обработанные данные обратно
ctx.putImageData(imageData, 0, 0);
}

Тестирование

Unit-тесты

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
}

Wasm-тесты

use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_greet() {
assert_eq!(greet("Test"), "Hello, Test!");
}
#[wasm_bindgen_test]
fn test_in_browser() {
let window = web_sys::window().expect("no window");
assert!(window.location().href().is_ok());
}

Запуск тестов:

# Запустите unit-тесты
cargo test
# Запустите Wasm-тесты в headless-браузере
wasm-pack test --headless --chrome
wasm-pack test --headless --firefox

Оптимизация

Минимизация размера

[profile.release]
opt-level = "z" # Оптимизация под размер
lto = true # Оптимизация на этапе линковки (LTO)
codegen-units = 1 # Один codegen unit
panic = "abort" # Abort при panic
strip = true # Удалите символы
# Используйте wasm-opt для дополнительной оптимизации
wasm-pack build --release
# Или вручную
wasm-opt -Oz pkg/my_wasm_bg.wasm -o pkg/my_wasm_bg.wasm

Измерение размера

# Проверьте размер файла
ls -lh pkg/*.wasm
# Проанализируйте секции
wasm-objdump -h pkg/my_wasm_bg.wasm

Интеграция с инструментами сборки

Webpack

// webpack.config.js
module.exports = {
experiments: {
asyncWebAssembly: true
}
};
// В вашем приложении
import * as wasm from 'my-wasm';
wasm.greet("Webpack");

Vite

// vite.config.js
import wasm from 'vite-plugin-wasm';
export default {
plugins: [wasm()]
};

Ключевые выводы

  1. Используйте wasm-pack: упрощает сборку и упаковку
  2. Начинайте с малого: сначала экспортируйте простые функции
  3. Управление памятью: вызывайте .free() для Rust struct’ов
  4. Оптимизируйте под размер: release-сборки с корректными настройками
  5. Тестируйте в браузере: некоторые API работают только в браузере
  6. Выбирайте правильный target: web, bundler или nodejs

WebAssembly с Rust открывает новые возможности для производительности веба — используйте это для вычислительно интенсивных задач, которые в JavaScript были бы слишком медленными.

 
 
 
Языки
Темы
Copyright © 1999 — 2026
Зетка Интерактив