Курс ЛР10 · Місія 10
Академічна↔ Сюжет С.І.Д.СРС до ЛР⌨️ команди 14 · 1 нова

Лабораторна робота № 10. Ядро на Rust у WASM на сторінці з відтворюваною збіркою

Мета

Навчитися компілювати Rust-код у WebAssembly за допомогою wasm-bindgen/wasm-pack, підключати отриманий модуль до HTML-сторінки, передавати між Rust і JavaScript складні типи даних (рядки) та налаштовувати повністю відтворювану (reproducible) збірку фінального модуля в ізольованому Docker-контейнері.

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Теоретичні відомості

wasm-bindgen — інструмент, що генерує парний код по обидва боки межі Rust↔JavaScript: у самому .wasm-модулі та у супровідному .js-файлі («glue code»). Функція, позначена атрибутом #[wasm_bindgen], стає викликаною напряму з JavaScript, ніби це звичайна JS-функція — усю роботу з передачею значень через межу мов бере на себе згенерований код.

Прості числові типи (i32, f64) відповідають типам значень WASM напряму й передаються без копіювання. Складніші типи, зокрема &str і String, вимагають маршалінгу: рядок Rust живе у лінійній пам'яті WASM-модуля (тема лабораторної № 8), а JavaScript отримує пару «вказівник + довжина» і сам декодує байти UTF-8 у JS-рядок (через TextDecoder у згенерованому glue-коді) або навпаки — кодує JS-рядок у байти лінійної пам'яті перед викликом (TextEncoder).

wasm-pack — конвеєр збірки, що об'єднує виклик cargo build --target wasm32-unknown-unknown, запуск wasm-bindgen CLI та пакування результату в теку pkg/ з .wasm-бінарником, .js-обгорткою та типами .d.ts. Прапорець --target web готує результат для прямого підключення через <script type="module"> без збирача.

Відтворювана збірка (reproducible build) — властивість, за якої той самий вихідний код, зібраний двічі (навіть на різних машинах), дає побайтово ідентичний результат. Це досягається фіксацією версій усіх інструментів (компілятора Rust, wasm-bindgen CLI) і залежностей (Cargo.lock) та збіркою в ізольованому середовищі — Docker-контейнері з конкретним тегом базового образу (не latest), щоб виключити вплив локального оточення розробника. Для системного й безпекового ПЗ це дозволяє довести відповідність опублікованого бінарника опублікованому вихідному коду.

Обладнання та програмне забезпечення

Порядок виконання

  1. Ініціалізувати Cargo-проєкт бібліотечного типу (cargo new --lib sid_core), додати залежність wasm-bindgen за специфікацією starter/README.md. Очікуваний результат: проєкт з crate-type = ["cdylib"] і точно зафіксованою версією wasm-bindgen.
  2. Написати мінімальну функцію рівня 1 з атрибутом #[wasm_bindgen], зібрати: wasm-pack build --target web. Очікуваний результат: тека pkg/ з sid_core.js і sid_core_bg.wasm, без помилок збірки.
  3. Підключити результат (pkg/) до index.html (шаблон надається в starter/), відкрити через локальний HTTP-сервер і перевірити виклик у DevTools. Очікуваний результат: сторінка виводить коректний результат echo_number(21).
  4. Для рівня 2 — реалізувати функцію, що приймає рядок з командою (&str) і повертає рядок-відповідь (String), підключити до UI-елемента на сторінці.
  5. Для рівня 3 — написати Dockerfile з фіксованою версією образу Rust, що виконує wasm-pack build всередині контейнера; переконатися, що дві послідовні збірки дають ідентичний за хешем (sha256sum) .wasm-файл.
  6. Запустити локальний скрипт перевірки з autograder/, переконатися, що всі перевірки рівня 1 проходять, перш ніж здавати роботу.
РІВЕНЬ 1 · «3» · ПЕРШОКУРСНИК

Рівень 1 (оцінка 3)

Зібрати ядро на Rust у формат WebAssembly (echo_number(x: i32) -> i32, повертає x * 2), підключити до HTML-сторінки через wasm-pack build --target web і відтворити виклик у браузері.

РІВЕНЬ 2 · «4» · МАГІСТР

Рівень 2 (оцінка 4, захист)

Налаштувати передачу складних типів даних (рядків з командами) між Rust та JS: реалізувати handle_command(cmd: &str) -> String з мінімум двома різними командами, підключити до полів UI. На захисті — пояснити механізм маршалінгу &str/String через лінійну пам'ять модуля.

РІВЕНЬ 3 · «5» · ЛЕГЕНДА

Рівень 3 (оцінка 5, розширення)

Налаштувати повністю відтворювану збірку фінального Wasm-модуля в ізольованому Docker-контейнері: Dockerfile з зафіксованими версіями образу і wasm-pack, що встановлює лише тулчейн (сама збірка запускається через docker run, а не запечена в образ RUN-кроком — інакше друга «незалежна» збірка непомітно поверне артефакт із кешу першого шару), і скрипт, що запускає дві незалежні збірки та звіряє sha256sum результатів. На захисті — продемонструвати однаковий хеш.

🧪 РОЗІБРАНИЙ ПРИКЛАД

Розібраний приклад

Cargo.toml і src/lib.rs рівня 1:

[package]
name = "sid_core"
version = "0.1.0"
edition = "2021"

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

[dependencies]
wasm-bindgen = "=0.2.92"
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn echo_number(x: i32) -> i32 {
    x * 2
}
wasm-pack build --target web
python3 -m http.server 8080

Маршалінг рядків рівня 2:

#[wasm_bindgen]
pub fn handle_command(cmd: &str) -> String {
    match cmd {
        "status" => String::from("ядро активне"),
        _ => String::from("невідома команда"),
    }
}

Відтворювана збірка рівня 3 — Dockerfile (лише фіксований тулчейн; збірку виконує docker run, а не RUN у образі, щоб кожен запуск справді компілював код заново):

FROM rust:1.79-slim
RUN rustup target add wasm32-unknown-unknown
RUN cargo install wasm-pack --version 0.13.0 --locked
WORKDIR /src

Перевірка відтворюваності:

docker build -t sid-core-build .

rm -rf out1 out2 target
docker run --rm -v "$(pwd):/src" sid-core-build \
  wasm-pack build --target web --release --out-dir out1

rm -rf target
docker run --rm -v "$(pwd):/src" sid-core-build \
  wasm-pack build --target web --release --out-dir out2

sha256sum out1/sid_core_bg.wasm out2/sid_core_bg.wasm

Однакові хеші підтверджують відтворюваність збірки.

❓ КОНТРОЛЬНІ ПИТАННЯ

Контрольні питання

  1. Що саме генерує wasm-bindgen по обидва боки межі Rust↔JavaScript?
  2. Чому прості числові типи передаються через межу без копіювання, а рядки — ні?
  3. Що робить прапорець --target web у wasm-pack build?
  4. Що таке «відтворювана збірка» і чому недостатньо просто зафіксувати версію Rust?
  5. Навіщо в Dockerfile для відтворюваної збірки уникати тега latest для базового образу?
  6. Як перевірити, що дві збірки дали побайтово ідентичний результат?
  7. Чим ризикована ситуація, коли опублікований .wasm-бінарник не відповідає опублікованому вихідному коду?
  8. Яка з трьох меж курсу (контейнер / володіння Rust / лінійна пам'ять WASM) задіяна в цій лабораторній і як саме?

Парні теми СРС

Критерії оцінювання та форма звіту

Здається тег submit/lab10 з Cargo-проєктом (Cargo.toml, src/lib.rs, Cargo.lock), index.html, і для рівня 3 — Dockerfile. Автотести рівня 1 (див. autograder/README.md) перевіряють: проєкт компілюється у WASM без помилок, згенерований pkg/ містить очікувану експортовану функцію з правильною сигнатурою, виклик з Node.js дає коректний результат. Рівні 2 і 3 захищаються усно: студент показує передачу рядка через DevTools console і — для рівня 3 — демонструє однаковий хеш .wasm-файлу після двох незалежних збірок у контейнері. Звіт додає скріншоти консолі з викликом функції, UI з обробленою командою і (рівень 3) виведення sha256sum двох збірок.

⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 14 · 1 нова

Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.

wasm-pack build
wasm-pack build --target web — компілює у wasm32 і кладе pkg/*_bg.wasm та pkg/*.js для підключення на сторінці.
wasm-pack
Збирає крейт Rust у пакет для браузера: .wasm + JS-обгортка (glue code) через wasm-bindgen.
python3
Інтерпретатор Python; тут — python3 -m http.server 80, найкоротший спосіб підняти тестовий HTTP-сервер.
docker build
Збирає образ за Dockerfile (docker build -t ім'я:тег . — крапка = контекст збірки; -f — інший Dockerfile).
docker
Клієнт Docker: усі дії з образами й контейнерами — через його підкоманди (docker <підкоманда> --help — довідка).
rmнове
Видаляє файли; rm -rf dir — каталог рекурсивно і без запитань (обережно: відміни немає).
docker run
Створює й запускає контейнер з образу (docker run -it ubuntu:24.04 bash — інтерактивно з терміналом; -d — у фоні; --rm — видалити після виходу; -p 8080:80 — прокинути порт).
sha256sum
Обчислює хеш SHA-256 файлу — «відбиток», за яким видно, чи два файли байт-у-байт однакові.
wasm-bindgen
Генерує JS-обгортку для модуля .wasm із Rust (його викликає wasm-pack; версія CLI має збігатися з крейтом wasm-bindgen).
cargo build
Компілює проєкт у target/debug/; --release — з оптимізаціями у target/release/.
cargo
Система збірки й менеджер пакетів Rust: створює проєкт, тягне залежності (крейти), збирає, тестує, запускає.
rustup target
Додає ціль компіляції для іншої платформи (rustup target add wasm32-unknown-unknown — щоб збирати у WebAssembly).
rustup
Встановлювач і менеджер версій Rust: тулчейни, цілі (targets), компоненти.
cargo new
Створює новий проєкт із Cargo.toml і src/main.rs (cargo new lockpick; --lib — бібліотека).

💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.