Rust та WebAssembly у 2026: Wasm, wasm-bindgen і питання для співбесіди
Практичний посібник з Rust WebAssembly з wasm-pack та wasm-bindgen. Створення, оптимізація та тестування Wasm модулів з прикладами та питаннями для співбесіди.

Rust WebAssembly (Wasm) забезпечує майже нативну продуктивність веб-застосунків шляхом компіляції коду Rust у бінарний формат, який працює в браузерах та серверних середовищах. Поєднання гарантій безпеки пам'яті Rust з ізольованим виконанням WebAssembly створює потужну основу для компонентів веб-застосунків, критичних до продуктивності — від обробки зображень до криптографічних операцій.
Rust компілюється в WebAssembly через wasm-pack та wasm-bindgen, створюючи модулі, які JavaScript може безпосередньо імпортувати та викликати. Робочий процес включає написання функцій Rust, позначення їх атрибутом #[wasm_bindgen], компіляцію в .wasm та імпорт згенерованого JavaScript-коду в будь-який веб-застосунок.
Налаштування Rust для розробки WebAssembly
Інструментарій Rust потребує цілі wasm32-unknown-unknown та wasm-pack для побудови модулів WebAssembly. Ці інструменти автоматично обробляють компіляцію, оптимізацію та генерацію прив'язок JavaScript.
# install-wasm-tools.sh
# Додайте ціль WebAssembly до rustup
rustup target add wasm32-unknown-unknown
# Встановіть wasm-pack для побудови та пакування
cargo install wasm-pack
# Перевірте встановлення
wasm-pack --versionПісля встановлення необхідно створити нову бібліотеку, налаштовану для виводу WebAssembly. Файл Cargo.toml потребує специфічних налаштувань для сумісності з Wasm.
# Cargo.toml
[package]
name = "wasm-calculator"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2.100"
[profile.release]
opt-level = "z" # Оптимізація для розміру
lto = true # Оптимізація часу лінкуванняТип бібліотеки cdylib створює динамічну бібліотеку, придатну для WebAssembly. Оптимізації профілю release значно зменшують розмір кінцевого файлу .wasm — часто на 50% або більше порівняно з неоптимізованими збірками.
Розуміння wasm-bindgen та інтеропераційності з JavaScript
Бібліотека wasm-bindgen створює міст між Rust та JavaScript шляхом автоматичної генерації типобезпечних прив'язок. Функції, позначені #[wasm_bindgen], стають доступними для виклику з JavaScript, тоді як функції JavaScript можна імпортувати в Rust.
use wasm_bindgen::prelude::*;
// Експортувати цю функцію до JavaScript
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
// Базові випадки для рекурсії
match n {
0 => 0,
1 => 1,
// Рекурсивне обчислення з оптимізацією хвостового виклику
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
// Імпортувати console.log з JavaScript
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
// Експортувати функцію, що використовує console.log
#[wasm_bindgen]
pub fn greet(name: &str) {
log(&format!("Привіт, {}!", name));
}Блок extern "C" оголошує функції JavaScript, які код Rust може викликати. Атрибут js_namespace вказує об'єкт JavaScript, що містить функцію. Ця двонаправлена прив'язка дозволяє безшовну інтеграцію між логікою Rust та API JavaScript.
Побудова та пакування модулів WebAssembly
Команда wasm-pack build компілює Rust у WebAssembly та генерує обгортковий код JavaScript. Різні цілі підходять для різних середовищ JavaScript.
# build-wasm.sh
# Побудова для бандлерів, таких як webpack, Vite або Rollup
wasm-pack build --target bundler --release
# Побудова для нативних ES модулів (браузер)
wasm-pack build --target web --release
# Побудова для Node.js
wasm-pack build --target nodejs --releaseКожна ціль створює різний вивід у директорії pkg/. Ціль bundler працює з сучасними JavaScript бандлерами, що підтримують ES модулі та імпорти WebAssembly. Ціль web створює код, який завантажується безпосередньо в браузерах через <script type="module">.
// Імпорт згенерованого модуля WebAssembly
import init, { fibonacci, greet } from './pkg/wasm_calculator.js';
async function run() {
// Ініціалізація модуля WebAssembly
await init();
// Виклик експортованих функцій Rust
const result = fibonacci(20);
console.log(`fibonacci(20) = ${result}`);
greet('WebAssembly');
}
run();Функція init() асинхронно завантажує та компілює файл .wasm. Після ініціалізації експортовані функції поводяться як звичайні функції JavaScript, а wasm-bindgen автоматично обробляє перетворення типів.
Готовий до співбесід з Rust?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Робота зі складними типами та пам'яттю
WebAssembly оперує на лінійній пам'яті — неперервному блоці байтів. Передача складних типів між Rust та JavaScript вимагає серіалізації або прямої маніпуляції пам'яттю. Бібліотека wasm-bindgen автоматично обробляє більшість поширених типів.
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
// Увімкнути підтримку serde для wasm-bindgen
#[derive(Serialize, Deserialize)]
pub struct Point {
pub x: f64,
pub y: f64,
}
// Приймати та повертати об'єкти JavaScript
#[wasm_bindgen]
pub fn distance(a: JsValue, b: JsValue) -> Result<f64, JsValue> {
// Десеріалізувати об'єкти JavaScript у структури Rust
let point_a: Point = serde_wasm_bindgen::from_value(a)?;
let point_b: Point = serde_wasm_bindgen::from_value(b)?;
// Обчислити евклідову відстань
let dx = point_b.x - point_a.x;
let dy = point_b.y - point_a.y;
Ok((dx * dx + dy * dy).sqrt())
}
// Повернути структуру Rust як об'єкт JavaScript
#[wasm_bindgen]
pub fn midpoint(a: JsValue, b: JsValue) -> Result<JsValue, JsValue> {
let point_a: Point = serde_wasm_bindgen::from_value(a)?;
let point_b: Point = serde_wasm_bindgen::from_value(b)?;
let mid = Point {
x: (point_a.x + point_b.x) / 2.0,
y: (point_a.y + point_b.y) / 2.0,
};
// Серіалізувати структуру Rust в об'єкт JavaScript
Ok(serde_wasm_bindgen::to_value(&mid)?)
}Бібліотека serde-wasm-bindgen (додайте до Cargo.toml: serde-wasm-bindgen = "0.6" та serde = { version = "1.0", features = ["derive"] }) ефективно конвертує між об'єктами JavaScript та типами Rust. Для критичного до продуктивності коду прямий доступ до пам'яті через бібліотеки js_sys та web_sys усуває накладні витрати серіалізації.
Оптимізація продуктивності та розміру WebAssembly
Модулі WebAssembly виграють від кількох технік оптимізації. Зменшення розміру покращує час завантаження, тоді як алгоритмічні оптимізації покращують продуктивність виконання. Інструмент wasm-opt з Binaryen надає додаткові оптимізації після компіляції.
# optimize-wasm.sh
# Встановіть binaryen для wasm-opt
# На macOS: brew install binaryen
# На Ubuntu: apt install binaryen
# Застосуйте агресивні оптимізації розміру
wasm-opt -Oz -o pkg/wasm_calculator_opt.wasm pkg/wasm_calculator_bg.wasm
# Застосуйте оптимізації швидкості замість цього
wasm-opt -O3 -o pkg/wasm_calculator_fast.wasm pkg/wasm_calculator_bg.wasmДодаткові налаштування Cargo.toml ще більше зменшують розмір виводу:
# Cargo.toml - додаткові оптимізації
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = trueЦі налаштування створюють найменший можливий вивід шляхом увімкнення оптимізації часу лінкування, зменшення паралельної генерації коду та видалення символів налагодження. Простий модуль Wasm може зменшитися зі 100KB до менш ніж 20KB при належній оптимізації.
Практичний приклад використання: обробка зображень у браузері
WebAssembly відмінно справляється з обчислювально інтенсивними завданнями, такими як обробка зображень. Наступний приклад застосовує фільтр відтінків сірого до даних зображення, демонструючи прямий доступ до пам'яті для максимальної продуктивності.
use wasm_bindgen::prelude::*;
use wasm_bindgen::Clamped;
#[wasm_bindgen]
pub fn grayscale(data: Clamped<Vec<u8>>, width: u32, height: u32) -> Clamped<Vec<u8>> {
let mut pixels = data.0;
let len = (width * height * 4) as usize;
// Обробити кожен піксель (формат RGBA)
for i in (0..len).step_by(4) {
// Обчислити яскравість, використовуючи стандартні коефіцієнти
let r = pixels[i] as f32;
let g = pixels[i + 1] as f32;
let b = pixels[i + 2] as f32;
let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
// Встановити канали RGB на значення відтінку сірого
pixels[i] = gray; // R
pixels[i + 1] = gray; // G
pixels[i + 2] = gray; // B
// Альфа-канал (i + 3) залишається незмінним
}
Clamped(pixels)
}Сторона JavaScript витягує піксельні дані з canvas, передає їх до Wasm та рендерить результат:
import init, { grayscale } from './pkg/image_processor.js';
async function applyGrayscale(canvas) {
await init();
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Передати піксельні дані до WebAssembly
const result = grayscale(imageData.data, canvas.width, canvas.height);
// Створити новий ImageData з обробленими пікселями
const processed = new ImageData(result, canvas.width, canvas.height);
ctx.putImageData(processed, 0, 0);
}Цей підхід обробляє мільйони пікселів за секунду — значно швидше, ніж еквівалентний код JavaScript. Бенчмарки на типовому ноутбуці показують прискорення в 3-5 разів для операцій із зображеннями.
Питання для співбесіди: концепції Rust WebAssembly
Розуміння концепцій Rust WebAssembly демонструє знання системного програмування. Ці питання часто з'являються на співбесідах для ролей, пов'язаних із продуктивністю веб-розробки.
П: Який зв'язок між wasm-bindgen та інтеропераційністю з JavaScript?
wasm-bindgen генерує JavaScript-код, який обробляє перетворення типів між Rust та JavaScript. Він створює JavaScript-обгортку для кожної експортованої функції Rust, керуючи виділенням пам'яті, кодуванням рядків та серіалізацією складних типів. Без wasm-bindgen розробникам довелося б вручну керувати лінійною пам'яттю WebAssembly та реалізовувати власні протоколи серіалізації.
П: Чому WebAssembly використовує лінійну пам'ять і які її наслідки?
Лінійна пам'ять WebAssembly — це неперервний масив байтів, що може змінювати розмір, до якого мають доступ як JavaScript, так і код Wasm. Ця конструкція забезпечує безпеку пам'яті через перевірку меж та ізоляцію. Наслідком є те, що передача складних структур даних вимагає або копіювання даних у лінійну пам'ять, або використання посилань з ретельним керуванням часом життя. Модель володіння Rust добре узгоджується з цим обмеженням, оскільки запобігає висячим вказівникам та гонкам даних під час компіляції.
П: Як режим no_std у Rust сприяє розробці WebAssembly?
Атрибут #![no_std] виключає стандартну бібліотеку Rust, створюючи менші бінарні файли Wasm. Без std розробники використовують бібліотеки core та alloc для базової функціональності. Цей підхід драматично зменшує розмір пакунка — з ~100KB до ~10KB для простих модулів. Компроміс полягає у втраті зручностей, таких як String, Vec та стандартний I/O, хоча alloc надає типи, що виділяються на купі, за потреби.
Для глибшого дослідження асинхронних шаблонів Rust, які доповнюють розробку WebAssembly, перегляньте питання про модуль async/await.
Налагодження та тестування модулів WebAssembly
Ефективне тестування вимагає як модульних тестів на стороні Rust, так і інтеграційних тестів у браузері. Команда wasm-pack test запускає тести в безголовому середовищі браузера.
#[cfg(test)]
mod tests {
use super::*;
use wasm_bindgen_test::*;
// Налаштувати тести для запуску в браузері
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_fibonacci() {
assert_eq!(fibonacci(0), 0);
assert_eq!(fibonacci(1), 1);
assert_eq!(fibonacci(10), 55);
}
#[wasm_bindgen_test]
fn test_greet() {
// Цей тест перевіряє, що greet не панікує
greet("Test User");
}
}Запустіть браузерні тести з Chrome або Firefox:
# test-wasm.sh
# Запустити тести в безголовому Chrome
wasm-pack test --headless --chrome
# Запустити тести в безголовому Firefox
wasm-pack test --headless --firefoxДля налагодження увімкніть source maps та вивід консолі. Бібліотека console_error_panic_hook надає зрозумілі повідомлення про паніку в консолі браузера:
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn init_panic_hook() {
console_error_panic_hook::set_once();
}Додайте до Cargo.toml: console_error_panic_hook = "0.1". Це налаштування відображає повідомлення про паніку Rust в інструментах розробника браузера замість криптичних помилок WebAssembly.
Висновок
- Rust компілюється в WebAssembly через
wasm-pack, створюючи модулі, що викликаються з JavaScript з автоматичними перетвореннями типів - Атрибут
#[wasm_bindgen]позначає функції для експорту; блокиextern "C"імпортують функції JavaScript до Rust - Оптимізації розміру (
opt-level = "z",lto = true,wasm-opt -Oz) зменшують розмір пакунка на 50-80% - Складні типи передаються через
serde-wasm-bindgenдля JSON-подібної серіалізації або через прямий доступ до пам'яті для максимальної продуктивності - Обробка зображень, криптографія та обчислювально інтенсивні алгоритми досягають прискорення в 3-5 разів порівняно з реалізаціями JavaScript
- Браузерне тестування з
wasm-pack test --headlessперевіряє як логіку Rust, так і інтеграцію з JavaScript
Для пов'язаних тем Rust перегляньте питання співбесіди про Cargo та екосистему та повний посібник підготовки до співбесіди з Rust.
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Поділитися
Пов'язані статті

Обробка Помилок у Rust 2026: Result, Option, thiserror та anyhow
Повний посібник з обробки помилок у Rust з використанням Result, Option, оператора ? та бібліотек thiserror і anyhow. Найкращі практики та патерни для продакшн застосунків.

Розумні вказівники в Rust: Box, Rc, Arc і RefCell у 2026
Розумні вказівники Rust Box, Rc, Arc і RefCell з поясненнями, компільованими прикладами 2026 року, таблицею вибору та типовими питаннями співбесід.

Rust 2026: Трейти, Дженерики та Просунуті Питання для Співбесід
Повний посібник з трейтів та дженериків у Rust з Edition 2024: trait upcasting, AsyncFn, RPITIT, синтаксис use<> та просунуті питання для технічних співбесід з прикладами коду.