Введение
Мне уже давно хотелось попробовать использовать Rust и WASM вместе. Причин для этого была масса: небольшой размер пакета, низкоуровневый доступ с надёжной производительностью и все те преимущества, которые идут в комплекте с Rust (сильная типобезопасность, абстракции с нулевой стоимостью и т. д.). Так что, когда я оказался свободен на пару недель от других проектов и мне представилась возможность что-нибудь поизучать, я просто не мог не ухватиться за эту возможность и не испытать Rust и WASM вместе!
То, что происходило со мной в течение этих двух недель, можно назвать чем-то вроде американских горок для программиста — аттракциона, через который все программисты проходят много раз. Но когда пришло время подробно описать в статье свой опыт, я вдруг заметил, что это были не просто американские горки: всё происшедшее идеально соответствовало структуре ромкома, забавной романтической истории! Итак, для лучшего объяснения и понимания того, как работает это официально не поддерживаемое сочетание упаковщика для веб-приложений и языка системного программирования, в нашем изложении мы будем придерживаться стандартного формата романтической комедии из 10 частей.
Часть 1: несбывшееся желание
Ещё одна причина, почему мне хотелось использовать Rust и WASM, — они абсолютно новые и блестящие, плюс было бы неплохо подключить программу на Rust к хорошему веб-интерфейсу. Вот только есть одна проблема: Rust и WASM официально поддерживаются только с Webpack в качестве бандлера. Для меня Webpack была той бывшей из романтической комедии: отношения с ней хуже некуда, и исправить их было совершенно нереально. Так что она представлялась мне необходимым злом, которое придётся использовать ради достижения цели — сделать что-то с помощью Rust, моей утраченной любви.
Часть 2: первая встреча
И вот я скрепя сердце начинаю клонировать шаблон Rust WASM Webpack, мысленно возвращаясь к своему предыдущему проекту и вспоминая наши с Webpack скандалы и перепалки при компилировании одностраничного приложения. Список зависимостей растёт с каждым плагином. Я нажимаю Ctrl + C за Ctrl + C… «Нет, мне нужна другая!» — думаю я. И тут меня осенило: «Parcel! Я помню, она говорила что-то о WASM!». Сразу перехожу на сайт Parcel. И вот она, её-то мне и не хватало! Быстро устанавливаю её командой npm install
, и вот уже я по уши влюблен!
Часть 3: счастливы вместе
Ещё пара команд npm init
и npm install -D parcel-bundler
, и готово! Parcel из коробки поддерживает импорт файлов .rs в JS и TS, поэтому в простом шаблоне HTML5 с main.js я делаю именно это. Такой файл Rust содержит простую функцию, которая при задании двух чисел возвращает их сумму, не позволяя компилятору искажать имя функции! JS вызывает эту функцию и отображает результат в DOM. Пример простой, но включает, кажется, всё, что мне нужно…
Часть 4: первые трудности
Но, как и в большинстве романтических комедий, первые трудности ставят отношения под вопрос. Для Rust и Parcel такой проблемой стало возвращение или получение строк в функциях. Что бы я ни делал, ничто не срабатывало, и бесконечные undefined
заполонили мою консоль. Хотя, казалось бы, есть решение — пакет “wasm_bindgen” с хорошей поддержкой, он предоставляет полифил для многого из того, что отсутствует между Rust и JS! Итак, делаем проект на Rust, используя cargo.toml, добавляем крейт wasm_bingen, импортируем и запускаем… но подождите. Parcel, похоже, не работает с wasm_bindgen даже с помощью плагина, который кто-то в предложил на GitHub issue в качестве решения… И что теперь?
Часть 5: путь преодоления
После нескольких минут лихорадочного поиска в Google и беглого ознакомления с выложенными на GitHub issues вопросами и ошибками, различными инструкциями и материалами в блогах я наконец-то нашёл альтернативный пакет stdweb. Здесь есть большая часть функциональности wasm_bindgen и удобная инструкция для настройки его с Parcel! Быстро переключаем пакеты в cargo.toml, вносим небольшие изменения в код, и вот строки снова возвращаются и принимаются в этом простом приложении. А теперь сделаем что-нибудь посложнее… простой симулятор генетического алгоритма!
Часть 6: новые трудности
Итак, делаем новый проект: Parcel установлен, Rust-модуль создан, stdweb тоже установлен. Всё готово. Приступим! Идея проста: сделать структуру на Rust для представления генетического алгоритма, добавить к ней кое-каких методов, чтобы получить популяцию или смоделировать поколение, а затем просто инстанцировать и использовать её в JS. Не так это трудно, как кажется (но что-то настораживает). Давайте просто создадим структуру, как бы инстанцируемую в JS, добавим методов в структуру… но, подождите… Что опять? Похоже, экспорт структур при использовании stdweb и parcel работает в лучшем случае нестабильно. Неужели всё придётся начинать сначала?
Часть 7: выбор
Кажется, что всё потеряно, у меня больше нет подходящих Rust-пакетов, а консоль завалена ошибками. Неужели ничего нельзя сделать? В отчаянной попытке пробую сам вручную скомпилировать и импортировать файл .wasm, но после пяти внесённых в файл Rust изменений бросаю это занятие… Опять лезу в GitHub issue, где снова и снова в качестве решения появляется webpack. Может, пора признать поражение и вернуться назад?
Часть 8: кризис
«Придётся возвращаться к Webpack», — думаю я, опустошённый этой неудачей, открывая шаблон Webpack Rust.
Часть 9: озарение
Пока клонировался репозиторий шаблона Webpack Rust, в последний раз зашёл в поисковик проверить, нет ли чего по моим старым запросам, надеясь разве что на чудо. А это что такое? Неужто в GitHub issue появился новый комментарий о Parcel и WASM_Bindgen, которого раньше не было? Поисковый индекс, должно быть, только сейчас признал его релевантным. Автор комментария связал здесь шаблон для Rust, WASM_Bindgen и Parcel! Хвала разработчикам поисковых систем, проект теперь можно спасти!
Часть 10: решение
И всё это время решение было у меня под носом, в этом репозитории на GitHub rustwasm. Я быстро клонировал репозиторий, следуя инструкциям по настройке, и всё прошло без проблем. У нашей истории счастливое завершение: в последний момент поиск дал решение, которое мы искали, и получилось, как в сказке, когда туфелька идеально подошла Золушке.
Давайте же сделаем что-нибудь полезное с помощью этой находки. Мне не хотелось возиться с фронтендом и горбатиться над SCSS, пытаясь придать ему благообразности, поэтому я обратился к старому другу, utility-first CSS-фреймворку TailWindCSS, который я уже предварительно настроил с PostCSS и Parcel. Здесь всё готово, и остаётся создать простой макет с боковой панелью для настройки моделирования и главной панелью для хранения его результатов. Определившись с внешним видом страницы, приступаю к созданию компонентов TypeScript для контроля и отображения моделирования.
Наконец, проверив работоспособность сайта на тестовых данных из простого set_interval
, начинаю подключать его к WASM. В итоге всё оказывается очень просто: импортируем объект module
из cargo.toml
проектов на Rust, а затем к нему подключаются все структуры и функции! Небольшая подгонка настроек и тестирование, и что бы вы думали? Всё это работает! Немного очистки, затем разворачиваем его на Firebase, и с тех пор размещается он там долго и счастливо.
Заключение
Статья получилась в лёгком, живом стиле, а говоря о проекте, я искренне наслаждался каждой его минутой, с удовольствием следил за каждым его взлетом и падением. Но какие впечатления остались от совместного использования Rust и Parcel? Могу лишь сказать, что это будет настоящим наслаждением… как только вы найдёте правильные ресурсы. Parcel здорово облегчает работу, ведь для большинства проектов ему не требуется никакой настройки. И хотя он может быть не таким быстрым на более крупных проектах, конкуренцию составить сможет в 9 случаях из 10!
Что касается Rust и WASM, они оправдали мои ожидания и даже превзошли их. Rust всегда был языком, на котором я программировал с удовольствием, хотя для меня это не так просто, тем не менее я продолжаю это делать. Однако если пытаться отыскать какие-то недостатки при работе с ним, то это отсутствие автодополнения ввода в экспортируемом модуле JS. Возможно, это не проблема, когда пишешь крошечный компилируемый файл на Rust, но на больших проектах, где используются Rust, WASM и Parcel, она даёт о себе знать.
И напоследок, если вас когда-нибудь подначивал внутренний голос: «А не попробовать ли Rust или WASM?», я бы очень рекомендовал попробовать и, возможно, использовать вместе с ними Parcel во избежание тех эмоциональных американских горок, которые мне пришлось испытать для этого!
Читайте также:
- Кросс-компиляция программ Rust для запуска на маршрутизаторе
- Rust и разработка кроссплатформенных решений для мобильных устройств
- Как компилировать ZXing C++ в Wasm, используя WASI SDK в Linux
Перевод статьи Alex Eales: Parcel and Rust: A WASM Romcom