- PVSM.RU - https://www.pvsm.ru -
Это моя первая статья на Хабре. На первый взгляд может показаться, что она хорошо структурирована и грамотна, но не переживайте — Илон Маск до меня не добрался, это не очередной AI-спам.
Просто я такой. Можете зайти в мои open source проекты [1] и посмотреть на код — сами убедитесь. При желании можете связаться и я расскажу про свои загоны. Короче, я педант, люблю красиво. Не надо думать что это "AI spam" — это просто я такой дотошный.
Структура статьи:
Как и зачем я начал делать SDK
Почему Rust на фронтенде имеет смысл
Технические детали и архитектура
Реальный опыт использования
Честные минусы и проблемы
Практические примеры и туториалы
А теперь поехали по тексту.
Три месяца назад я делал очередной Telegram бот и подумал: "А на *** мне опять тащить npm, React и весь этот зоопарк зависимостей?" У меня бэкенд на Rust, модели данных на Rust, валидаторы на Rust, API на Rust... А фронтенд — на JavaScript. Приходится дублировать типы, поддерживать два разных стека, синхронизировать зависимости.
Это реально бесило.
Я открыл документацию Telegram WebApp API [2], посмотрел на официальный JS SDK из проекта telegram-apps и решил: "Ладно, попробую обернуть это в Rust через wasm-bindgen. Сколько там может быть? Пара методов?"
Через неделю у меня был рабочий прототип. Ещё через месяц — полноценный SDK на 6300+ строк кода с поддержкой всех методов API. Теперь это опубликованный крейт на crates.io [3] с растущим числом скачиваний.
И знаете что? Это реально работает. Не "работает в теории", не "можно использовать для toy-проектов", а именно работает в продакшене.
Ссылка на проект: github.com/RAprogramm/telegram-webapp-sdk [4]
Все мне говорили:
"Мало библиотек, экосистема сырая"
"Toolchain тяжелый, сборка долгая"
"Всё равно будут мосты с JS — смысла нет"
"Developer Experience не такой удобный"
// Это не скомпилируется
let button_color: u32 = "red"; // ❌ error[E0308]: mismatched types
// А это в JavaScript упадёт в runtime
Telegram.WebApp.MainButton.setColor(undefined); // 💥
Вызовы API Telegram проверяются на этапе компиляции. Нет места для undefined is not a function, Cannot read property of null, и прочих JS-радостей.
Одна и та же структура данных работает и на сервере, и на клиенте. Одна кодовая база, одни валидаторы, один набор тестов. Никаких TypeScript-интерфейсов, которые нужно держать в синхронности с бэкендом.
WASM предсказуем. Нет garbage collection пауз, нет "внезапных" тормозов из-за Vue/React reconciliation. Всё детерминировано и измеряемо.
Помните истории про left-pad [5], про colors и faker [6]? С Rust крейтами такого не происходит. cargo.io имеет политику immutability — опубликованные версии нельзя удалить или изменить.
Без воды: 6,284 строк кода, 55 файлов, 2,469 строк документации. Покрывает полностью весь Telegram WebApp API версии 9.2. За 3 месяца вышло 15 релизов.
SDK поддерживает:
MainButton, SecondaryButton, BackButton
Theme и Viewport управление
Cloud Storage (для хранения данных в облаке Telegram)
Biometric Authentication
Haptic Feedback
Device Sensors (акселерометр, гироскоп)
Location Manager
Invoice Payments
И ещё куча всего
Я не изобретал велосипед. Я взял официальный API и обернул его в типобезопасную Rust-обёртку через wasm-bindgen.
JavaScript API (Telegram.WebApp)
↓
wasm-bindgen биндинги
↓
Rust API (типобезопасный)
↓
Yew/Leptos интеграция
telegram_webapp_sdk::webapp — базовые методы, initData, события
main_button, secondary_button, back_button — управление кнопками
theme, viewport — внешний вид и размеры
haptic_feedback — вибрация устройства
cloud_storage — хранилище в облаке Telegram
biometric — биометрическая аутентификация
location_manager, accelerometer, gyroscope — датчики
Макросы: telegram_app!, telegram_page!, telegram_router!
Интеграции с Yew и Leptos
Mock-окружение для локальной разработки
Самый сложный момент был с обработкой событий. В JavaScript это просто:
Telegram.WebApp.onEvent('mainButtonClicked', () => {
console.log("Clicked!");
});
Но в Rust через wasm-bindgen нельзя просто так передать замыкание — оно должно жить достаточно долго. Первая версия приводила к segfault при клике на кнопку. Вот это было весело: "Рустовая безопасность памяти", ага.
// ❌ Это НЕ РАБОТАЛО — замыкание умирает сразу
app.on_event("mainButtonClicked", || {
console::log_1(&"Clicked!".into());
})?;
Проблема в том, что wasm-bindgen требует, чтобы замыкание было 'static. Мне пришлось делать обёртку EventHandle, которая управляет временем жизни через Box::leak() и держит указатель на замыкание.
// ✅ Это РАБОТАЕТ
let handle = app.on_event("mainButtonClicked", |_| {
console::log_1(&"Clicked!".into());
})?;
// handle автоматически управляет памятью
Эта одна проблема заняла 3 дня дебага с gdb, чтения исходников wasm-bindgen и проклятий в адрес lifetime анализатора. Три. Дня.
Но зато теперь это работает надёжно. И когда я это починил, я понял всю мощь Rust: проблемы жёсткие, но когда решаешь — они решены навсегда. В JS я бы словил этот баг в проде через неделю, потратил бы час на поиски, пофиксил бы... и забыл через месяц, откуда он вообще взялся.
Покажу на живом примере. Управление главной кнопкой:
Telegram.WebApp.MainButton.setText("Pay $10");
Telegram.WebApp.MainButton.setParams({
color: "#FF0000",
text_color: "#FFFFFF"
});
Telegram.WebApp.MainButton.show();
Telegram.WebApp.MainButton.onClick(() => {
console.log("Processing payment...");
// тут может прилететь что угодно
});
use telegram_webapp_sdk::main_button::{set_text, show, on_click, set_params};
use telegram_webapp_sdk::webapp::MainButtonParams;
fn setup_payment_button() {
set_text("Pay $10").unwrap();
set_params(&MainButtonParams {
color: Some("#FF0000".into()),
text_color: Some("#FFFFFF".into()),
..Default::default()
}).unwrap();
show().unwrap();
on_click(|| {
console::log_1(&"Processing payment...".into());
// типы проверены на компиляции
}).unwrap();
}
В JavaScript вы можете передать null, неправильный тип, забыть поле — и узнаете об этом в runtime. В Rust код просто не скомпилируется, если вы передадите что-то неверное.
SDK работает с Yew (React-like) и Leptos (Solid.js-like). Вот как выглядит код на Yew:
use telegram_webapp_sdk::yew::{use_telegram_context, BottomButton};
use yew::prelude::*;
#[function_component(PaymentPage)]
fn payment_page() -> Html {
let ctx = use_telegram_context().expect("Telegram context");
html! {
<div>
<p>{ format!("User ID: {:?}", ctx.init_data.user) }</p>
<BottomButton
text="Pay $10"
color="#FF0000"
on_click={Callback::from(|_| { /* обработка */ })}
/>
</div>
}
}
Если вы предпочитаете Leptos — он тоже поддерживается из коробки.
Одна из проблем разработки Telegram WebApp — нужно запускать приложение внутри Telegram. Дебажить неудобно.
Поэтому я добавил mock-окружение. Создаёте конфиг файл с тестовыми данными пользователя и темы, подключаете фичу mock — и всё, можно запускать приложение локально в браузере без Telegram. Mock автоматически эмулирует весь Telegram.WebApp API.
Это сэкономило мне недели времени на разработку.
Чтобы не писать boilerplate, я добавил макросы telegram_app!, telegram_page! и telegram_router!. Они автоматически инициализируют WebApp, загружают mock в debug-режиме, настраивают роутинг и запускают приложение. Меньше шаблонного кода — больше пользы.
Не всё идеально. Вот с чем я столкнулся:
В JavaScript у вас отличные sourcemaps, и вы видите точную строку ошибки. В WASM sourcemaps работают плохо. Дебажить сложнее.
Решение: я использую console_error_panic_hook для более читаемых panic-сообщений.
В Vite/Webpack есть HMR — изменения применяются мгновенно. В Rust нужно пересобирать WASM. Это занимает ~5-10 секунд.
Решение: использую trunk с --watch, но это всё равно медленнее чем HMR.
Даже после оптимизации WASM весит больше чем минифицированный JS. Минимальное Yew приложение ~150KB (gzipped ~50KB) против React ~80KB (gzipped ~30KB).
Решение: для больших приложений разница нивелируется. Плюс есть wasm-opt для дополнительной оптимизации. Но да, "Hello World" на Rust тяжелее.
В React есть Material-UI, Ant Design, Chakra. В Rust есть... ну, yew-components и пара библиотек. Всё.
Решение: пишу компоненты сам или использую CSS-фреймворки (Tailwind работает отлично). В итоге даже хорошо — меньше зависимостей, больше контроля.
rust-analyzer иногда тупит на сложных макросах. Автокомплит не всегда работает идеально.
Решение: терпение и cargo check.
Детальных бенчмарков не делал, но вот что заметил:
Сборка prod: React+TS ~45 сек, Rust+Yew ~12 сек
Размер бандла: React ~245 KB (gzip), Rust ~156 KB (gzip)
Runtime ошибки (первая неделя в проде): React — 3 бага, Rust — 0
Самое главное — все баги ловятся на компиляции, не в проде.
Использую SDK в нескольких проектах. Один из них — бот для управления подписками: список активных подписок, оплата через Telegram Invoice API, история транзакций, биометрическая аутентификация для критичных операций.
Стек: Rust везде. Backend (Axum), Frontend (Yew + telegram-webapp-sdk), PostgreSQL, Docker + Fly.io.
Что работает отлично: единые типы между бэком и фронтом, валидация на compile-time, Cloud Storage для кеша, Haptic Feedback для UX.
Что было сложно: настройка CI/CD для WASM, интеграция с Telegram Payments, дебаг в Safari на iOS (sourcemaps бесполезны).
Добавляете в Cargo.toml:
telegram-webapp-sdk = { version = "0.2", features = ["yew", "mock"] }
Пишете компонент:
use telegram_webapp_sdk::yew::use_telegram_context;
use yew::prelude::*;
#[function_component(App)]
fn app() -> Html {
let ctx = use_telegram_context().expect("Telegram context");
html! {
<div>
<h1>{ "Hello, Telegram!" }</h1>
<p>{ format!("User: {:?}", ctx.init_data.user) }</p>
</div>
}
}
Собираете через trunk serve — и всё работает.
Полная документация в README [7] и на docs.rs [8].
Продолжаю использовать SDK в продакшене и активно развиваю проект.
Планы:
Поддержка Telegram Stars API
Улучшить mock-окружение (визуальный конфигуратор)
Больше примеров и туториалов
Интеграция с Dioxus
Если вы пишете на Rust, делаете Telegram боты, устали от npm-зоопарка и хотите типобезопасность на фронтенде — попробуйте: telegram-webapp-sdk = "0.2"
GitHub: https://github.com/RAprogramm/telegram-webapp-sdk [4]
crates.io: https://crates.io/crates/telegram-webapp-sdk [3]
docs.rs: https://docs.rs/telegram-webapp-sdk [8]
Telegram WebApp API: https://core.telegram.org/bots/webapps [2]
Rust на фронтенде — это не "будущее", это настоящее. Да, экосистема ещё развивается. Да, есть шероховатости. Но если вы хотите типобезопасность, единый стек с бэкендом, предсказуемую производительность и меньше runtime-багов — WASM + Rust это реальная альтернатива JavaScript-стеку.
Я написал SDK, опубликовал его, использую в проде. И это работает. Не в теории, а прямо сейчас.
Если вы дочитали до конца — поздравляю! Значит, AI не вложил в вас дурные мысли о том, что Rust на фронтенде — это безумие 😄
Кстати, изначально хотел сделать краткую структурированную статейку, чисто информативную. Но видимо не ценится такое... Поэтому получилось как получилось — с подробностями, примерами и честным рассказом о проблемах.
Просто попробуйте. Предсказуемый фронтенд на Rust — это круто. Возможно, вам зайдёт так же, как зашло мне.
Буду рад PR, issue и просто фидбеку в GitHub репозитории [4]. Особенно если найдёте баги — их точно ещё есть :-)
Спасибо за внимание!
Автор: RAprogramm
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/rust/434066
Ссылки в тексте:
[1] open source проекты: https://github.com/RAprogramm
[2] документацию Telegram WebApp API: https://core.telegram.org/bots/webapps
[3] crates.io: https://crates.io/crates/telegram-webapp-sdk
[4] github.com/RAprogramm/telegram-webapp-sdk: https://github.com/RAprogramm/telegram-webapp-sdk
[5] left-pad: https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code
[6] colors и faker: https://www.theverge.com/2022/1/9/22874949/developer-corrupts-open-source-libraries-projects-affected
[7] README: https://github.com/RAprogramm/telegram-webapp-sdk/blob/main/README.md
[8] docs.rs: https://docs.rs/telegram-webapp-sdk
[9] Источник: https://habr.com/ru/articles/958402/?utm_source=habrahabr&utm_medium=rss&utm_campaign=958402
Нажмите здесь для печати.